From 590a16ee8d2759ca072efaee11aa2f8f227aee1f Mon Sep 17 00:00:00 2001 From: polytan02 Date: Tue, 7 Feb 2017 18:02:28 +0000 Subject: [PATCH] Update to follow YEP 3.3 We download sources and check md5 instead of storing the sources in github --- scripts/install | 11 +- scripts/remove | 0 scripts/upgrade | 10 +- sources/INSTALL | 290 -- sources/INSTALL_UPDATE_FROM_GIT | 21 - sources/LICENSE | 696 ---- sources/NOTES | 4 - sources/README.md | 114 - sources/autodiscover/INSTALL | 177 - sources/autodiscover/autodiscover.php | 256 -- sources/autodiscover/response.xml | 19 - sources/backend/caldav/AUTHOR | 3 - sources/backend/caldav/REQUIREMENTS | 8 - sources/backend/caldav/caldav.php | 1528 -------- sources/backend/caldav/config.php | 74 - sources/backend/carddav/README | 12 - sources/backend/carddav/REQUIREMENTS | 5 - sources/backend/carddav/THANKS | 8 - sources/backend/carddav/carddav.php | 1462 -------- sources/backend/carddav/config.php | 109 - sources/backend/combined/combined.php | 718 ---- sources/backend/combined/config.php | 116 - sources/backend/combined/exporter.php | 189 - sources/backend/combined/importer.php | 360 -- sources/backend/imap/README | 28 - sources/backend/imap/REQUIREMENTS | 7 - sources/backend/imap/THANKS | 4 - sources/backend/imap/config.php | 227 -- sources/backend/imap/imap.php | 2707 -------------- sources/backend/imap/mime_calendar.php | 357 -- sources/backend/imap/mime_encode.php | 307 -- sources/backend/imap/user_identity.php | 232 -- sources/backend/ldap/AUTHOR | 3 - sources/backend/ldap/config.php | 58 - sources/backend/ldap/ldap.php | 590 --- sources/backend/maildir/config.php | 49 - sources/backend/maildir/maildir.php | 716 ---- sources/backend/searchldap/config.php | 74 - sources/backend/searchldap/searchldap.php | 197 - sources/backend/vcarddir/config.php | 48 - sources/backend/vcarddir/vcarddir.php | 678 ---- sources/backend/zarafa/config.php | 49 - sources/backend/zarafa/exporter.php | 297 -- sources/backend/zarafa/icalparser.php | 199 - sources/backend/zarafa/importer.php | 696 ---- sources/backend/zarafa/listfolders.php | 192 - .../zarafa/mapi/class.baseexception.php | 225 -- .../zarafa/mapi/class.baserecurrence.php | 1944 ---------- .../zarafa/mapi/class.freebusypublish.php | 396 -- .../zarafa/mapi/class.mapiexception.php | 106 - .../zarafa/mapi/class.meetingrequest.php | 3197 ----------------- .../backend/zarafa/mapi/class.recurrence.php | 1576 -------- .../zarafa/mapi/class.taskrecurrence.php | 463 --- .../backend/zarafa/mapi/class.taskrequest.php | 1035 ------ sources/backend/zarafa/mapi/mapi.util.php | 338 -- sources/backend/zarafa/mapi/mapicode.php | 246 -- sources/backend/zarafa/mapi/mapidefs.php | 666 ---- sources/backend/zarafa/mapi/mapiguid.php | 72 - sources/backend/zarafa/mapi/mapitags.php | 1254 ------- sources/backend/zarafa/mapimapping.php | 526 --- sources/backend/zarafa/mapiphpwrapper.php | 234 -- sources/backend/zarafa/mapiprovider.php | 2682 -------------- sources/backend/zarafa/mapistreamwrapper.php | 146 - sources/backend/zarafa/mapiutils.php | 585 --- sources/backend/zarafa/tnefparser.php | 720 ---- sources/backend/zarafa/zarafa.php | 1901 ---------- sources/composer.json | 6 - sources/config.php | 369 -- sources/docker/README.md | 103 - sources/docker/basic.yml | 13 - sources/docker/nginx/Dockerfile | 16 - sources/docker/nginx/localhost.crt | 21 - sources/docker/nginx/localhost.key | 27 - sources/docker/nginx/nginx.conf | 91 - sources/docker/php-fpm/Dockerfile | 21 - sources/include/Auth/SASL.php | 148 - sources/include/Auth/SASL/Anonymous.php | 68 - sources/include/Auth/SASL/Common.php | 128 - sources/include/Auth/SASL/CramMD5.php | 65 - sources/include/Auth/SASL/DigestMD5.php | 194 - sources/include/Auth/SASL/External.php | 60 - sources/include/Auth/SASL/Login.php | 62 - sources/include/Auth/SASL/Plain.php | 60 - sources/include/Auth/SASL/SCRAM.php | 301 -- sources/include/Mail.php | 299 -- sources/include/Mail/mail.php | 193 - sources/include/Mail/sendmail.php | 197 - sources/include/Mail/smtp.php | 500 --- sources/include/Net/SMTP.php | 1303 ------- sources/include/Net/Socket.php | 726 ---- sources/include/iCalendar.php | 1799 ---------- sources/include/mimeDecode.php | 1217 ------- sources/include/mimePart.php | 1270 ------- sources/include/z_RFC822.php | 945 ----- sources/include/z_RTF.php | 704 ---- sources/include/z_caldav.php | 1019 ------ sources/include/z_carddav.php | 900 ----- sources/include/z_syslog.php | 108 - sources/index.php | 257 -- sources/lib/core/asdevice.php | 689 ---- sources/lib/core/bodypreference.php | 67 - sources/lib/core/changesmemorywrapper.php | 347 -- sources/lib/core/contentparameters.php | 139 - sources/lib/core/devicemanager.php | 903 ----- sources/lib/core/hierarchycache.php | 214 -- sources/lib/core/statemanager.php | 539 --- sources/lib/core/stateobject.php | 266 -- sources/lib/core/streamer.php | 451 --- sources/lib/core/streamimporter.php | 260 -- sources/lib/core/synccollections.php | 724 ---- sources/lib/core/syncparameters.php | 417 --- sources/lib/core/zlog.php | 291 -- sources/lib/core/zpush-utils.php | 73 - sources/lib/core/zpush.php | 876 ----- sources/lib/core/zpushdefs.php | 1075 ------ sources/lib/core/zsyslog.php | 81 - sources/lib/default/backend.php | 328 -- .../lib/default/diffbackend/diffbackend.php | 368 -- sources/lib/default/diffbackend/diffstate.php | 314 -- .../default/diffbackend/exportchangesdiff.php | 216 -- .../default/diffbackend/importchangesdiff.php | 278 -- sources/lib/default/filestatemachine.php | 705 ---- sources/lib/default/searchprovider.php | 124 - sources/lib/default/simplemutex.php | 76 - sources/lib/default/sqlstatemachine.php | 929 ----- .../authenticationrequiredexception.php | 50 - sources/lib/exceptions/fatalexception.php | 45 - .../fatalmisconfigurationexception.php | 44 - .../fatalnotimplementedexception.php | 45 - .../exceptions/httpreturncodeexception.php | 54 - .../nohierarchycacheavailableexception.php | 44 - .../lib/exceptions/nopostrequestexception.php | 49 - .../exceptions/notimplementedexception.php | 47 - .../provisioningrequiredexception.php | 49 - .../lib/exceptions/stateinvalidexception.php | 44 - .../lib/exceptions/statenotfoundexception.php | 45 - .../statenotyetavailableexception.php | 44 - sources/lib/exceptions/statusexception.php | 46 - .../exceptions/syncobjectbrokenexception.php | 71 - sources/lib/exceptions/wbxmlexception.php | 44 - sources/lib/exceptions/zpushexception.php | 73 - sources/lib/interface/ibackend.php | 310 -- sources/lib/interface/ichanges.php | 84 - sources/lib/interface/iexportchanges.php | 74 - sources/lib/interface/iimportchanges.php | 141 - sources/lib/interface/iloopdetection.php | 221 -- sources/lib/interface/ipingtracking.php | 12 - sources/lib/interface/isearchprovider.php | 105 - sources/lib/interface/istatemachine.php | 236 -- sources/lib/interface/itopcollector.php | 66 - sources/lib/ipc/interprocessstorage.php | 40 - sources/lib/ipc/redis/InterProcessRedis.php | 38 - sources/lib/ipc/redis/LoopDetectionRedis.php | 674 ---- sources/lib/ipc/redis/PingTrackingRedis.php | 47 - sources/lib/ipc/redis/REQUIREMENTS | 3 - sources/lib/ipc/redis/TopCollectorRedis.php | 161 - sources/lib/ipc/shm/interprocessdata.php | 275 -- sources/lib/ipc/shm/loopdetection.php | 934 ----- sources/lib/ipc/shm/pingtracking.php | 154 - sources/lib/ipc/shm/topcollector.php | 298 -- sources/lib/request/folderchange.php | 246 -- sources/lib/request/foldersync.php | 260 -- sources/lib/request/getattachment.php | 84 - sources/lib/request/gethierarchy.php | 80 - sources/lib/request/getitemestimate.php | 286 -- sources/lib/request/itemoperations.php | 389 -- sources/lib/request/meetingresponse.php | 131 - sources/lib/request/moveitems.php | 137 - sources/lib/request/notify.php | 82 - sources/lib/request/ping.php | 227 -- sources/lib/request/provisioning.php | 229 -- sources/lib/request/request.php | 625 ---- sources/lib/request/requestprocessor.php | 168 - sources/lib/request/resolverecipients.php | 103 - sources/lib/request/search.php | 447 --- sources/lib/request/sendmail.php | 137 - sources/lib/request/settings.php | 231 -- sources/lib/request/sync.php | 1257 ------- sources/lib/request/validatecert.php | 89 - sources/lib/syncobjects/syncappointment.php | 221 -- .../syncobjects/syncappointmentexception.php | 76 - sources/lib/syncobjects/syncattachment.php | 76 - sources/lib/syncobjects/syncattendee.php | 69 - .../lib/syncobjects/syncbaseattachment.php | 70 - sources/lib/syncobjects/syncbasebody.php | 67 - sources/lib/syncobjects/synccontact.php | 205 -- .../lib/syncobjects/syncdeviceinformation.php | 84 - .../lib/syncobjects/syncdevicepassword.php | 62 - sources/lib/syncobjects/syncfolder.php | 78 - .../syncitemoperationsattachment.php | 61 - sources/lib/syncobjects/syncmail.php | 199 - sources/lib/syncobjects/syncmailflags.php | 98 - .../lib/syncobjects/syncmeetingrequest.php | 139 - .../syncmeetingrequestrecurrence.php | 119 - sources/lib/syncobjects/syncnote.php | 74 - sources/lib/syncobjects/syncobject.php | 421 --- sources/lib/syncobjects/syncoof.php | 82 - sources/lib/syncobjects/syncoofmessage.php | 80 - sources/lib/syncobjects/syncprovisioning.php | 302 -- sources/lib/syncobjects/syncrecurrence.php | 119 - .../lib/syncobjects/syncresolverecipient.php | 76 - .../lib/syncobjects/syncresolverecipients.php | 74 - .../syncresolverecipientsavailability.php | 65 - .../syncresolverecipientscertificates.php | 73 - .../syncresolverecipientsoptions.php | 71 - .../syncresolverecipientspicture.php | 65 - sources/lib/syncobjects/syncsendmail.php | 85 - .../lib/syncobjects/syncsendmailsource.php | 66 - sources/lib/syncobjects/synctask.php | 186 - .../lib/syncobjects/synctaskrecurrence.php | 157 - .../lib/syncobjects/syncuserinformation.php | 77 - sources/lib/syncobjects/syncvalidatecert.php | 71 - sources/lib/utils/compat.php | 118 - sources/lib/utils/stringstreamwrapper.php | 65 - sources/lib/utils/timezoneutil.php | 1320 ------- sources/lib/utils/utils.php | 1069 ------ sources/lib/utils/zpushadmin.php | 721 ---- sources/lib/wbxml/wbxmldecoder.php | 476 --- sources/lib/wbxml/wbxmldefs.php | 757 ---- sources/lib/wbxml/wbxmlencoder.php | 513 --- sources/lib/webservice/webservice.php | 91 - sources/lib/webservice/webservicedevice.php | 133 - sources/lib/webservice/webserviceusers.php | 100 - sources/source_md5 | 1 + sources/source_sha1 | 1 + sources/source_url | 1 + sources/sql/mysql.sql | 28 - sources/testing/samples/meeting_reply_rim.txt | 43 - sources/testing/samples/meeting_request.txt | 53 - .../testing/samples/meeting_request_rim.txt | 38 - sources/testing/samples/messages/emoticon.txt | 28 - .../samples/messages/emoticon_base64.txt | 39 - .../samples/messages/emoticon_subject.txt | 20 - sources/testing/samples/messages/french.txt | 48 - sources/testing/samples/messages/m0001.txt | 31 - sources/testing/samples/messages/m0002.txt | 31 - sources/testing/samples/messages/m0003.txt | 30 - sources/testing/samples/messages/m0004.txt | 31 - sources/testing/samples/messages/m0005.txt | 31 - sources/testing/samples/messages/m0006.txt | 34 - sources/testing/samples/messages/m0007.txt | 32 - sources/testing/samples/messages/m0008.txt | 32 - sources/testing/samples/messages/m0009.txt | 27 - sources/testing/samples/messages/m0010.txt | 30 - sources/testing/samples/messages/m0011.txt | 135 - sources/testing/samples/messages/m0012.txt | 43 - sources/testing/samples/messages/m0013.txt | 83 - sources/testing/samples/messages/m0014.txt | 72 - sources/testing/samples/messages/m0015.txt | 144 - sources/testing/samples/messages/m0016.txt | 156 - sources/testing/samples/messages/m0017.txt | 188 - sources/testing/samples/messages/m0018.txt | 131 - sources/testing/samples/messages/m0019.txt | 3 - sources/testing/samples/messages/m1001.txt | 32 - sources/testing/samples/messages/m1002.txt | 61 - sources/testing/samples/messages/m1003.txt | 137 - sources/testing/samples/messages/m1004.txt | 133 - sources/testing/samples/messages/m1005.txt | 212 -- sources/testing/samples/messages/m1006.txt | 171 - sources/testing/samples/messages/m1007.txt | 17 - sources/testing/samples/messages/m1008.txt | 27 - sources/testing/samples/messages/m1009.txt | 111 - sources/testing/samples/messages/m1010.txt | 33 - sources/testing/samples/messages/m1011.txt | 26 - sources/testing/samples/messages/m1012.txt | 28 - sources/testing/samples/messages/m1013.txt | 51 - sources/testing/samples/messages/m1014.txt | 155 - sources/testing/samples/messages/m1015.txt | 60 - sources/testing/samples/messages/m1016.txt | 58 - sources/testing/samples/messages/m2001.txt | 29 - sources/testing/samples/messages/m2002.txt | 63 - sources/testing/samples/messages/m2003.txt | 36 - sources/testing/samples/messages/m2004.txt | 138 - sources/testing/samples/messages/m2005.txt | 198 - sources/testing/samples/messages/m2006.txt | 109 - sources/testing/samples/messages/m2007.txt | 171 - sources/testing/samples/messages/m2008.txt | 161 - sources/testing/samples/messages/m2009.txt | 109 - sources/testing/samples/messages/m2010.txt | 102 - sources/testing/samples/messages/m2011.txt | 95 - sources/testing/samples/messages/m2012.txt | 108 - sources/testing/samples/messages/m2013.txt | 84 - sources/testing/samples/messages/m2014.txt | 36 - sources/testing/samples/messages/m2015.txt | 21 - sources/testing/samples/messages/m2016.txt | 18 - sources/testing/samples/messages/m3001.txt | 96 - sources/testing/samples/messages/m3002.txt | 26 - sources/testing/samples/messages/m3003.txt | 57 - sources/testing/samples/messages/m3004.txt | 42 - sources/testing/samples/messages/m4000.txt | 629 ---- sources/testing/samples/messages/smime001.txt | 40 - sources/testing/samples/messages/smime002.txt | 18 - .../messages/zpush-html-preview-bug.txt | 66 - sources/testing/samples/smime.txt | 63 - sources/testing/testing-bug68532fixed.php | 22 - sources/testing/testing-caldav.php | 48 - sources/testing/testing-caldav_search.php | 39 - sources/testing/testing-carddav.php | 63 - sources/testing/testing-forward.php | 218 -- sources/testing/testing-imap.php | 8 - sources/testing/testing-imap_date.php | 51 - sources/testing/testing-imap_folder_list.php | 188 - sources/testing/testing-imap_from.php | 133 - sources/testing/testing-imap_issue_120.php | 70 - sources/testing/testing-imap_list_155.php | 18 - sources/testing/testing-imap_meeting.php | 68 - .../testing/testing-imap_meeting_method.php | 25 - sources/testing/testing-imap_meeting_tzid.php | 33 - sources/testing/testing-imap_overview.php | 35 - sources/testing/testing-imap_smtp.php | 53 - sources/testing/testing-issue_164.php | 27 - sources/testing/testing-mime-mail-parse.php | 57 - sources/testing/testing-mime-split.php | 22 - sources/testing/testing-mime.php | 179 - sources/testing/testing-mime_preview.php | 24 - sources/testing/testing-mimetype.php | 38 - sources/testing/testing-preg_split.php | 11 - sources/testing/testing-redis.php | 16 - sources/testing/testing-rfc822_199.php | 15 - sources/testing/testing-ternary.php | 28 - .../tools/fix-meetings-2.0.8+2.1.0-ios7.py | 90 - sources/tools/migrate-2.0.x-2.1.0.php | 215 -- sources/tools/printwbxml.php | 84 - sources/vendor/autoload.php | 7 - sources/vendor/composer/ClassLoader.php | 413 --- sources/vendor/composer/autoload_classmap.php | 155 - sources/vendor/composer/autoload_files.php | 13 - .../vendor/composer/autoload_namespaces.php | 9 - sources/vendor/composer/autoload_psr4.php | 9 - sources/vendor/composer/autoload_real.php | 55 - sources/version.php | 45 - sources/z-push-admin.php | 900 ----- sources/z-push-top.php | 761 ---- 333 files changed, 21 insertions(+), 84778 deletions(-) mode change 100644 => 100755 scripts/install mode change 100644 => 100755 scripts/remove mode change 100644 => 100755 scripts/upgrade delete mode 100644 sources/INSTALL delete mode 100644 sources/INSTALL_UPDATE_FROM_GIT delete mode 100644 sources/LICENSE delete mode 100644 sources/NOTES delete mode 100644 sources/README.md delete mode 100644 sources/autodiscover/INSTALL delete mode 100644 sources/autodiscover/autodiscover.php delete mode 100644 sources/autodiscover/response.xml delete mode 100644 sources/backend/caldav/AUTHOR delete mode 100644 sources/backend/caldav/REQUIREMENTS delete mode 100644 sources/backend/caldav/caldav.php delete mode 100644 sources/backend/caldav/config.php delete mode 100644 sources/backend/carddav/README delete mode 100644 sources/backend/carddav/REQUIREMENTS delete mode 100644 sources/backend/carddav/THANKS delete mode 100644 sources/backend/carddav/carddav.php delete mode 100644 sources/backend/carddav/config.php delete mode 100644 sources/backend/combined/combined.php delete mode 100644 sources/backend/combined/config.php delete mode 100644 sources/backend/combined/exporter.php delete mode 100644 sources/backend/combined/importer.php delete mode 100644 sources/backend/imap/README delete mode 100644 sources/backend/imap/REQUIREMENTS delete mode 100644 sources/backend/imap/THANKS delete mode 100644 sources/backend/imap/config.php delete mode 100644 sources/backend/imap/imap.php delete mode 100644 sources/backend/imap/mime_calendar.php delete mode 100644 sources/backend/imap/mime_encode.php delete mode 100644 sources/backend/imap/user_identity.php delete mode 100644 sources/backend/ldap/AUTHOR delete mode 100644 sources/backend/ldap/config.php delete mode 100644 sources/backend/ldap/ldap.php delete mode 100644 sources/backend/maildir/config.php delete mode 100644 sources/backend/maildir/maildir.php delete mode 100644 sources/backend/searchldap/config.php delete mode 100644 sources/backend/searchldap/searchldap.php delete mode 100644 sources/backend/vcarddir/config.php delete mode 100644 sources/backend/vcarddir/vcarddir.php delete mode 100644 sources/backend/zarafa/config.php delete mode 100644 sources/backend/zarafa/exporter.php delete mode 100644 sources/backend/zarafa/icalparser.php delete mode 100644 sources/backend/zarafa/importer.php delete mode 100755 sources/backend/zarafa/listfolders.php delete mode 100644 sources/backend/zarafa/mapi/class.baseexception.php delete mode 100644 sources/backend/zarafa/mapi/class.baserecurrence.php delete mode 100644 sources/backend/zarafa/mapi/class.freebusypublish.php delete mode 100644 sources/backend/zarafa/mapi/class.mapiexception.php delete mode 100644 sources/backend/zarafa/mapi/class.meetingrequest.php delete mode 100644 sources/backend/zarafa/mapi/class.recurrence.php delete mode 100644 sources/backend/zarafa/mapi/class.taskrecurrence.php delete mode 100644 sources/backend/zarafa/mapi/class.taskrequest.php delete mode 100644 sources/backend/zarafa/mapi/mapi.util.php delete mode 100644 sources/backend/zarafa/mapi/mapicode.php delete mode 100644 sources/backend/zarafa/mapi/mapidefs.php delete mode 100644 sources/backend/zarafa/mapi/mapiguid.php delete mode 100644 sources/backend/zarafa/mapi/mapitags.php delete mode 100644 sources/backend/zarafa/mapimapping.php delete mode 100644 sources/backend/zarafa/mapiphpwrapper.php delete mode 100644 sources/backend/zarafa/mapiprovider.php delete mode 100644 sources/backend/zarafa/mapistreamwrapper.php delete mode 100644 sources/backend/zarafa/mapiutils.php delete mode 100644 sources/backend/zarafa/tnefparser.php delete mode 100644 sources/backend/zarafa/zarafa.php delete mode 100644 sources/composer.json delete mode 100644 sources/config.php delete mode 100644 sources/docker/README.md delete mode 100644 sources/docker/basic.yml delete mode 100644 sources/docker/nginx/Dockerfile delete mode 100644 sources/docker/nginx/localhost.crt delete mode 100644 sources/docker/nginx/localhost.key delete mode 100644 sources/docker/nginx/nginx.conf delete mode 100644 sources/docker/php-fpm/Dockerfile delete mode 100755 sources/include/Auth/SASL.php delete mode 100755 sources/include/Auth/SASL/Anonymous.php delete mode 100755 sources/include/Auth/SASL/Common.php delete mode 100755 sources/include/Auth/SASL/CramMD5.php delete mode 100755 sources/include/Auth/SASL/DigestMD5.php delete mode 100644 sources/include/Auth/SASL/External.php delete mode 100755 sources/include/Auth/SASL/Login.php delete mode 100755 sources/include/Auth/SASL/Plain.php delete mode 100644 sources/include/Auth/SASL/SCRAM.php delete mode 100644 sources/include/Mail.php delete mode 100644 sources/include/Mail/mail.php delete mode 100644 sources/include/Mail/sendmail.php delete mode 100644 sources/include/Mail/smtp.php delete mode 100644 sources/include/Net/SMTP.php delete mode 100644 sources/include/Net/Socket.php delete mode 100644 sources/include/iCalendar.php delete mode 100644 sources/include/mimeDecode.php delete mode 100644 sources/include/mimePart.php delete mode 100644 sources/include/z_RFC822.php delete mode 100644 sources/include/z_RTF.php delete mode 100644 sources/include/z_caldav.php delete mode 100644 sources/include/z_carddav.php delete mode 100644 sources/include/z_syslog.php delete mode 100644 sources/index.php delete mode 100644 sources/lib/core/asdevice.php delete mode 100644 sources/lib/core/bodypreference.php delete mode 100644 sources/lib/core/changesmemorywrapper.php delete mode 100644 sources/lib/core/contentparameters.php delete mode 100644 sources/lib/core/devicemanager.php delete mode 100644 sources/lib/core/hierarchycache.php delete mode 100644 sources/lib/core/statemanager.php delete mode 100644 sources/lib/core/stateobject.php delete mode 100644 sources/lib/core/streamer.php delete mode 100644 sources/lib/core/streamimporter.php delete mode 100644 sources/lib/core/synccollections.php delete mode 100644 sources/lib/core/syncparameters.php delete mode 100644 sources/lib/core/zlog.php delete mode 100644 sources/lib/core/zpush-utils.php delete mode 100644 sources/lib/core/zpush.php delete mode 100644 sources/lib/core/zpushdefs.php delete mode 100644 sources/lib/core/zsyslog.php delete mode 100644 sources/lib/default/backend.php delete mode 100644 sources/lib/default/diffbackend/diffbackend.php delete mode 100644 sources/lib/default/diffbackend/diffstate.php delete mode 100644 sources/lib/default/diffbackend/exportchangesdiff.php delete mode 100644 sources/lib/default/diffbackend/importchangesdiff.php delete mode 100644 sources/lib/default/filestatemachine.php delete mode 100644 sources/lib/default/searchprovider.php delete mode 100644 sources/lib/default/simplemutex.php delete mode 100644 sources/lib/default/sqlstatemachine.php delete mode 100644 sources/lib/exceptions/authenticationrequiredexception.php delete mode 100644 sources/lib/exceptions/fatalexception.php delete mode 100644 sources/lib/exceptions/fatalmisconfigurationexception.php delete mode 100644 sources/lib/exceptions/fatalnotimplementedexception.php delete mode 100644 sources/lib/exceptions/httpreturncodeexception.php delete mode 100644 sources/lib/exceptions/nohierarchycacheavailableexception.php delete mode 100644 sources/lib/exceptions/nopostrequestexception.php delete mode 100644 sources/lib/exceptions/notimplementedexception.php delete mode 100644 sources/lib/exceptions/provisioningrequiredexception.php delete mode 100644 sources/lib/exceptions/stateinvalidexception.php delete mode 100644 sources/lib/exceptions/statenotfoundexception.php delete mode 100644 sources/lib/exceptions/statenotyetavailableexception.php delete mode 100644 sources/lib/exceptions/statusexception.php delete mode 100644 sources/lib/exceptions/syncobjectbrokenexception.php delete mode 100644 sources/lib/exceptions/wbxmlexception.php delete mode 100644 sources/lib/exceptions/zpushexception.php delete mode 100644 sources/lib/interface/ibackend.php delete mode 100644 sources/lib/interface/ichanges.php delete mode 100644 sources/lib/interface/iexportchanges.php delete mode 100644 sources/lib/interface/iimportchanges.php delete mode 100644 sources/lib/interface/iloopdetection.php delete mode 100644 sources/lib/interface/ipingtracking.php delete mode 100644 sources/lib/interface/isearchprovider.php delete mode 100644 sources/lib/interface/istatemachine.php delete mode 100644 sources/lib/interface/itopcollector.php delete mode 100644 sources/lib/ipc/interprocessstorage.php delete mode 100644 sources/lib/ipc/redis/InterProcessRedis.php delete mode 100644 sources/lib/ipc/redis/LoopDetectionRedis.php delete mode 100644 sources/lib/ipc/redis/PingTrackingRedis.php delete mode 100644 sources/lib/ipc/redis/REQUIREMENTS delete mode 100644 sources/lib/ipc/redis/TopCollectorRedis.php delete mode 100644 sources/lib/ipc/shm/interprocessdata.php delete mode 100644 sources/lib/ipc/shm/loopdetection.php delete mode 100644 sources/lib/ipc/shm/pingtracking.php delete mode 100644 sources/lib/ipc/shm/topcollector.php delete mode 100644 sources/lib/request/folderchange.php delete mode 100644 sources/lib/request/foldersync.php delete mode 100644 sources/lib/request/getattachment.php delete mode 100644 sources/lib/request/gethierarchy.php delete mode 100644 sources/lib/request/getitemestimate.php delete mode 100644 sources/lib/request/itemoperations.php delete mode 100644 sources/lib/request/meetingresponse.php delete mode 100644 sources/lib/request/moveitems.php delete mode 100644 sources/lib/request/notify.php delete mode 100644 sources/lib/request/ping.php delete mode 100644 sources/lib/request/provisioning.php delete mode 100644 sources/lib/request/request.php delete mode 100644 sources/lib/request/requestprocessor.php delete mode 100755 sources/lib/request/resolverecipients.php delete mode 100644 sources/lib/request/search.php delete mode 100644 sources/lib/request/sendmail.php delete mode 100644 sources/lib/request/settings.php delete mode 100644 sources/lib/request/sync.php delete mode 100755 sources/lib/request/validatecert.php delete mode 100644 sources/lib/syncobjects/syncappointment.php delete mode 100644 sources/lib/syncobjects/syncappointmentexception.php delete mode 100644 sources/lib/syncobjects/syncattachment.php delete mode 100644 sources/lib/syncobjects/syncattendee.php delete mode 100644 sources/lib/syncobjects/syncbaseattachment.php delete mode 100644 sources/lib/syncobjects/syncbasebody.php delete mode 100644 sources/lib/syncobjects/synccontact.php delete mode 100644 sources/lib/syncobjects/syncdeviceinformation.php delete mode 100644 sources/lib/syncobjects/syncdevicepassword.php delete mode 100644 sources/lib/syncobjects/syncfolder.php delete mode 100644 sources/lib/syncobjects/syncitemoperationsattachment.php delete mode 100644 sources/lib/syncobjects/syncmail.php delete mode 100644 sources/lib/syncobjects/syncmailflags.php delete mode 100644 sources/lib/syncobjects/syncmeetingrequest.php delete mode 100644 sources/lib/syncobjects/syncmeetingrequestrecurrence.php delete mode 100644 sources/lib/syncobjects/syncnote.php delete mode 100644 sources/lib/syncobjects/syncobject.php delete mode 100644 sources/lib/syncobjects/syncoof.php delete mode 100644 sources/lib/syncobjects/syncoofmessage.php delete mode 100644 sources/lib/syncobjects/syncprovisioning.php delete mode 100644 sources/lib/syncobjects/syncrecurrence.php delete mode 100755 sources/lib/syncobjects/syncresolverecipient.php delete mode 100755 sources/lib/syncobjects/syncresolverecipients.php delete mode 100755 sources/lib/syncobjects/syncresolverecipientsavailability.php delete mode 100755 sources/lib/syncobjects/syncresolverecipientscertificates.php delete mode 100755 sources/lib/syncobjects/syncresolverecipientsoptions.php delete mode 100755 sources/lib/syncobjects/syncresolverecipientspicture.php delete mode 100644 sources/lib/syncobjects/syncsendmail.php delete mode 100644 sources/lib/syncobjects/syncsendmailsource.php delete mode 100644 sources/lib/syncobjects/synctask.php delete mode 100644 sources/lib/syncobjects/synctaskrecurrence.php delete mode 100644 sources/lib/syncobjects/syncuserinformation.php delete mode 100755 sources/lib/syncobjects/syncvalidatecert.php delete mode 100644 sources/lib/utils/compat.php delete mode 100644 sources/lib/utils/stringstreamwrapper.php delete mode 100644 sources/lib/utils/timezoneutil.php delete mode 100644 sources/lib/utils/utils.php delete mode 100644 sources/lib/utils/zpushadmin.php delete mode 100644 sources/lib/wbxml/wbxmldecoder.php delete mode 100644 sources/lib/wbxml/wbxmldefs.php delete mode 100644 sources/lib/wbxml/wbxmlencoder.php delete mode 100644 sources/lib/webservice/webservice.php delete mode 100644 sources/lib/webservice/webservicedevice.php delete mode 100644 sources/lib/webservice/webserviceusers.php create mode 100644 sources/source_md5 create mode 100644 sources/source_sha1 create mode 100644 sources/source_url delete mode 100644 sources/sql/mysql.sql delete mode 100644 sources/testing/samples/meeting_reply_rim.txt delete mode 100644 sources/testing/samples/meeting_request.txt delete mode 100644 sources/testing/samples/meeting_request_rim.txt delete mode 100644 sources/testing/samples/messages/emoticon.txt delete mode 100644 sources/testing/samples/messages/emoticon_base64.txt delete mode 100644 sources/testing/samples/messages/emoticon_subject.txt delete mode 100644 sources/testing/samples/messages/french.txt delete mode 100644 sources/testing/samples/messages/m0001.txt delete mode 100644 sources/testing/samples/messages/m0002.txt delete mode 100644 sources/testing/samples/messages/m0003.txt delete mode 100644 sources/testing/samples/messages/m0004.txt delete mode 100644 sources/testing/samples/messages/m0005.txt delete mode 100644 sources/testing/samples/messages/m0006.txt delete mode 100644 sources/testing/samples/messages/m0007.txt delete mode 100644 sources/testing/samples/messages/m0008.txt delete mode 100644 sources/testing/samples/messages/m0009.txt delete mode 100644 sources/testing/samples/messages/m0010.txt delete mode 100644 sources/testing/samples/messages/m0011.txt delete mode 100644 sources/testing/samples/messages/m0012.txt delete mode 100644 sources/testing/samples/messages/m0013.txt delete mode 100644 sources/testing/samples/messages/m0014.txt delete mode 100644 sources/testing/samples/messages/m0015.txt delete mode 100644 sources/testing/samples/messages/m0016.txt delete mode 100644 sources/testing/samples/messages/m0017.txt delete mode 100644 sources/testing/samples/messages/m0018.txt delete mode 100644 sources/testing/samples/messages/m0019.txt delete mode 100644 sources/testing/samples/messages/m1001.txt delete mode 100644 sources/testing/samples/messages/m1002.txt delete mode 100644 sources/testing/samples/messages/m1003.txt delete mode 100644 sources/testing/samples/messages/m1004.txt delete mode 100644 sources/testing/samples/messages/m1005.txt delete mode 100644 sources/testing/samples/messages/m1006.txt delete mode 100644 sources/testing/samples/messages/m1007.txt delete mode 100644 sources/testing/samples/messages/m1008.txt delete mode 100644 sources/testing/samples/messages/m1009.txt delete mode 100644 sources/testing/samples/messages/m1010.txt delete mode 100644 sources/testing/samples/messages/m1011.txt delete mode 100644 sources/testing/samples/messages/m1012.txt delete mode 100644 sources/testing/samples/messages/m1013.txt delete mode 100644 sources/testing/samples/messages/m1014.txt delete mode 100644 sources/testing/samples/messages/m1015.txt delete mode 100644 sources/testing/samples/messages/m1016.txt delete mode 100644 sources/testing/samples/messages/m2001.txt delete mode 100644 sources/testing/samples/messages/m2002.txt delete mode 100644 sources/testing/samples/messages/m2003.txt delete mode 100644 sources/testing/samples/messages/m2004.txt delete mode 100644 sources/testing/samples/messages/m2005.txt delete mode 100644 sources/testing/samples/messages/m2006.txt delete mode 100644 sources/testing/samples/messages/m2007.txt delete mode 100644 sources/testing/samples/messages/m2008.txt delete mode 100644 sources/testing/samples/messages/m2009.txt delete mode 100644 sources/testing/samples/messages/m2010.txt delete mode 100644 sources/testing/samples/messages/m2011.txt delete mode 100644 sources/testing/samples/messages/m2012.txt delete mode 100644 sources/testing/samples/messages/m2013.txt delete mode 100644 sources/testing/samples/messages/m2014.txt delete mode 100644 sources/testing/samples/messages/m2015.txt delete mode 100644 sources/testing/samples/messages/m2016.txt delete mode 100644 sources/testing/samples/messages/m3001.txt delete mode 100644 sources/testing/samples/messages/m3002.txt delete mode 100644 sources/testing/samples/messages/m3003.txt delete mode 100644 sources/testing/samples/messages/m3004.txt delete mode 100644 sources/testing/samples/messages/m4000.txt delete mode 100644 sources/testing/samples/messages/smime001.txt delete mode 100644 sources/testing/samples/messages/smime002.txt delete mode 100644 sources/testing/samples/messages/zpush-html-preview-bug.txt delete mode 100644 sources/testing/samples/smime.txt delete mode 100644 sources/testing/testing-bug68532fixed.php delete mode 100644 sources/testing/testing-caldav.php delete mode 100644 sources/testing/testing-caldav_search.php delete mode 100644 sources/testing/testing-carddav.php delete mode 100644 sources/testing/testing-forward.php delete mode 100644 sources/testing/testing-imap.php delete mode 100644 sources/testing/testing-imap_date.php delete mode 100644 sources/testing/testing-imap_folder_list.php delete mode 100644 sources/testing/testing-imap_from.php delete mode 100644 sources/testing/testing-imap_issue_120.php delete mode 100644 sources/testing/testing-imap_list_155.php delete mode 100644 sources/testing/testing-imap_meeting.php delete mode 100644 sources/testing/testing-imap_meeting_method.php delete mode 100644 sources/testing/testing-imap_meeting_tzid.php delete mode 100644 sources/testing/testing-imap_overview.php delete mode 100644 sources/testing/testing-imap_smtp.php delete mode 100644 sources/testing/testing-issue_164.php delete mode 100644 sources/testing/testing-mime-mail-parse.php delete mode 100644 sources/testing/testing-mime-split.php delete mode 100644 sources/testing/testing-mime.php delete mode 100644 sources/testing/testing-mime_preview.php delete mode 100644 sources/testing/testing-mimetype.php delete mode 100644 sources/testing/testing-preg_split.php delete mode 100644 sources/testing/testing-redis.php delete mode 100644 sources/testing/testing-rfc822_199.php delete mode 100644 sources/testing/testing-ternary.php delete mode 100644 sources/tools/fix-meetings-2.0.8+2.1.0-ios7.py delete mode 100644 sources/tools/migrate-2.0.x-2.1.0.php delete mode 100644 sources/tools/printwbxml.php delete mode 100644 sources/vendor/autoload.php delete mode 100644 sources/vendor/composer/ClassLoader.php delete mode 100644 sources/vendor/composer/autoload_classmap.php delete mode 100644 sources/vendor/composer/autoload_files.php delete mode 100644 sources/vendor/composer/autoload_namespaces.php delete mode 100644 sources/vendor/composer/autoload_psr4.php delete mode 100644 sources/vendor/composer/autoload_real.php delete mode 100644 sources/version.php delete mode 100755 sources/z-push-admin.php delete mode 100755 sources/z-push-top.php diff --git a/scripts/install b/scripts/install old mode 100644 new mode 100755 index 51822c9..a18ad92 --- a/scripts/install +++ b/scripts/install @@ -96,7 +96,15 @@ ynh_app_setting_set "$app" final_phpconf "$final_phpconf" # Copy files to the right place sudo mkdir -p $final_path -sudo cp -a ../sources/* $final_path + +# We download the sources and check the md5sum +SHA1=`sudo cat ../sources/source_sha1`; +sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_url +sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_md5 +sudo wget -nv -i ../sources/source_url -O Z-Push-contrib-${SHA1}.zip +sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false) +sudo unzip Z-Push-contrib-${SHA1}.zip -d ../sources/ +sudo cp -R ../sources/Z-Push-contrib-${SHA1}/* $final_path # Configuration sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php @@ -140,7 +148,6 @@ fi # Set permissions to z-push directory sudo chown -R www-data: $final_path - # Create log directory sudo mkdir -p $final_logpath sudo chmod 750 $final_logpath diff --git a/scripts/remove b/scripts/remove old mode 100644 new mode 100755 diff --git a/scripts/upgrade b/scripts/upgrade old mode 100644 new mode 100755 index c0bc626..0745fcc --- a/scripts/upgrade +++ b/scripts/upgrade @@ -57,7 +57,15 @@ sudo rm -f $final_phpconf # Copy files to the right place sudo mkdir -p $final_path -sudo cp -a ../sources/* $final_path + +# We download the sources and check the md5sum +SHA1=`sudo cat ../sources/source_sha1`; +sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_url +sed -i "s@SHA1TOCHANGE@$SHA1@g" ../sources/source_md5 +sudo wget -nv -i ../sources/source_url -O Z-Push-contrib-${SHA1}.zip +sudo md5sum -c ../sources/source_md5 --status || (echo "Corrupt source" >&2 && false) +sudo unzip Z-Push-contrib-${SHA1}.zip -d ../sources/ +sudo cp -R ../sources/Z-Push-contrib-${SHA1}/* $final_path # Configuration sed -i "s^define('TIMEZONE', .*^define('TIMEZONE', '$(cat /etc/timezone)');^" ../conf/config.php diff --git a/sources/INSTALL b/sources/INSTALL deleted file mode 100644 index f870974..0000000 --- a/sources/INSTALL +++ /dev/null @@ -1,290 +0,0 @@ -Installing Z-Push -====================== - -Requirements ------------- - -Z-Push 2 runs only on PHP 5.3 or later -A PEAR dependency as in previous versions does not exist in Z-Push 2. - -The PHP version requirement is met in these distributions and versions (or later). - -Debian 6.0 (squeeze) -Ubuntu 10.04 (LTS lucid) -RHEL/CentOS 6 (You can use SCL for newer version without overwriting system files: https://www.softwarecollections.org) -Fedora 12 (constantine) -OpenSuse 11.2 -Slackware 13.37 -FreeBSD 7.4 -OpenBSD 5.0 - -If your distribution is not listed here, you can check which PHP version -is default for it at http://distrowatch.com/. - -Additional informations can be found in the Zarafa Administrator Manual: -http://doc.zarafa.com/trunk/Administrator_Manual/en-US/html/_zpush.html - - -Additional php packages ----------------------- -To use the full featureset of Z-Push 2 and the z-push-top command line utility, -additional php packages are required. These provide SOAP support, access to -process control and shared memory. - -These packages vary in names between the distributions. -- Generally install the packages: php-cli php-soap -- On Suse (SLES & OpenSuse) install the packages: php53 php53-soap php53-pcntl php53-sysvshm php53-sysvsem php53-posix -- On RHEL based systems install the package: php-cli php-soap php-process - In order to install these packages you need to add an extra channel subscription - from the RHEL Server Optional channel. - - -Be aware that each backend can have their own requirements. Take a look to the -REQUIREMENTS file inside its folder for more information. - - -How to install --------------- - -To install Z-Push, simply untar the z-push archive, e.g. with: - tar -xzvf z-push-[version]-{buildnr}.tar.gz - -The tar contains a folder which has the following structure: - z-push-[version]-{buildnr} - -The contents of this folder should be copied to /usr/share/z-push. -In a case that /usr/share/z-push does not exist yet, create it with: - mkdir -p /usr/share/z-push - - cp -R z-push-[version]-{buildnr}/* /usr/share/z-push/ - -Edit the config.php file in the Z-Push directory to fit your needs. -If you intend to use Z-Push with Zarafa backend and Zarafa is installed -on the same server, it should work out of the box without changing anything. -Please also set your timezone in the config.php file. - -The parameters and their roles are also explained in the config.php file. - -By default the state directory is /var/lib/z-push, the log directory /var/log/z-push. -Make sure that these directories exist and are writeable for your webserver -process, so either change the owner of these directories to the UID of -your apache process or make the directories world writeable: - - chmod 755 /var/lib/z-push /var/log/z-push - chown apache:apache /var/lib/z-push /var/log/z-push - -For the default webserver user please refer to your distribution's manual. - -Now, you must configure Apache to redirect the URL -'Microsoft-Server-ActiveSync' to the index.php file in the Z-Push -directory. This can be done by adding the line: - - Alias /Microsoft-Server-ActiveSync /usr/share/z-push/index.php - -to your httpd.conf file. Make sure that you are adding the line to the -correct part of your Apache configuration, taking care of virtual hosts and -other Apache configurations. -Another possibility is to add this line to z-push.conf file inside the directory -which contents are automatically processed during the webserver start (by -default it is conf.d inside the /etc/apache2 or /etc/httpd depending on your -distribution). - -You have to reload your webserver after making these configurations. - -*WARNING* You CANNOT simply rename the z-push directory to -Microsoft-Server-ActiveSync. This will cause Apache to send redirects to the -mobile device, which will definitely break your mobile device synchronisation. - -Lastly, make sure that PHP has the following settings: - - php_flag magic_quotes_gpc off - php_flag register_globals off - php_flag magic_quotes_runtime off - php_flag short_open_tag on - -You can set this in the httpd.conf, in php.ini or in an .htaccess file in -the root of z-push. - -If you have several php applications on the same system, you could specify the -z-push directory so these settings are considered only there. - - php_flag magic_quotes_gpc off - php_flag register_globals off - php_flag magic_quotes_runtime off - php_flag short_open_tag on - - -If you don't set this up correctly, you will not be -able to login correctly via z-push. - -Please also set a memory_limit for php to 128M in php.ini. - -Z-Push writes files to your file system like logs or data from the -FileStateMachine (which is default). In order to make this possible, -you either need to disable the php-safe-mode in php.ini or .htaccess with - php_admin_flag safe_mode off -or configure it accordingly, so Z-Push is allowed to write to the -log and state directories. - -After doing this, you should be able to synchronize with your mobile device. - -To use the command line tools, access the installation directory -(usually /usr/share/z-push) and execute: - ./z-push-top.php and/or - ./z-push-admin.php - -To facilitate the access symbolic links can be created, by executing: - ln -s /usr/share/z-push/z-push-admin.php /usr/sbin/z-push-admin - ln -s /usr/share/z-push/z-push-top.php /usr/sbin/z-push-top - -With these symlinks in place the cli tools can be accessed from any -directory and without the php file extension. - - -Upgrade -------- -Upgrading to a newer Z-Push version follows the same path as the -initial installation. -When upgrading to a new minor version e.g. from Z-Push 1.4 to -Z-Push 1.4.1, the existing Z-Push directory can be overwritten -when extracting the archive. When installing a new major version -it is recommended to extract the tarball to another directory and -to copy the state from the existing installation. - -*Important* -It is crucial to always keep the data of the state directory in order -to ensure data consistency on already synchronized mobiles. - -Without the state information mobile devices, which already have an -ActiveSync profile, will receive duplicate items or the synchronization -will break completely. - -*Important* -Upgrading to Z-Push 2.X from 1.X it is not necessary to copy the state -directory because states are not compatible. However Z-Push 2 implements -a fully automatic resynchronizing of devices in the case states are -missing or faulty. - -*Important* -Downgrading from Z-Push 2.X to 1.X is not simple. As the states are not -compatible you would have to follow the procedure for a new installation -and re-create profiles on every device. - -*Important* -States of Z-Push 2.0 and Z-Push 2.1 are not compatible. A state migration -script called migrate-2.0.x-2.1.0.php is available in the tools folder. - -*Important* -When running Z-Push seperately from your Zarafa installation you had in -the past to configure MAPI_SERVER directly in the config.php of Z-Push. -This setting has now moved to the config.php file of the Zarafa backend -(backend/zarafa/config.php). - -Please also observe the published release notes of the new Z-Push version. -For some releases it is necessary to e.g. resynchronize the mobile. - - -S/MIME ------- -Z-Push supports signing and en-/decrypting of emails on mobile devices -since the version 2.0.7. - -*Important* -Currently only Android 4.X and higher and iOS 5 and higher devices are -known to support encryption/signing of emails. - -It might be possible that PHP functions require CA information in order -to validate certs. Therefore the CAINFO parameter in the config.php -must be configured properly. - -The major part of S/MIME deployment is the PKI setup. It includes the -public-private key/certificate obtaining, their management in directory -service and roll-out to the mobile devices. Individual certificates can -either be obtained from a local (company intern) or a public CA. There -are various public CAs offering certificates: commercial ones e.g. -Symantec or Comodo or community-driven e.g. CAcert.org. - -Both most popular directory services Microsoft Active Directory (MS AD) -and free open source solution OpenLDAP allow to save certificates. Private -keys/certificates reside in user’s directory or on a smartcard. Public -certificates are saved in directory. MS AD and OpenLDAP both use -serCertificate attribute to save it. - -In Active Directory the public key for contacts from GAB is saved in -PR_EMS_AB_TAGGED_X509_CERT (0x8C6A1102) property and if you save a key -in a contact it’s PR_USER_X509_CERTIFICATE (0x3A701102). - -In LDAP public key for contacts from GAB is saved in userCertificate -property. It should be mapped to 0x3A220102 in ldap.propmap.cfg -(0x3A220102 = userCertificate). Make sure it looks like this in LDAP: - -userCertificate;binary - MIIFGjCCBAKgAwIBAgIQbRnqpxlPa… - -*Important* -It is strongly recommended to use MS AD or LDAP to manage certificates. -Other user plugin options like db or unix might not work correctly and -are not supported. - -For in-depth information please refer to: -http://www.zarafa.com/blog/post/2013/05/smime-z-push-signing-and-en-decrypting-emails-mobile-devices - -Setting up your mobile device ------------------------------ - -This is simply a case of adding an 'exchange server' to your activesync -server list, specifying the IP address of the Z-Push's apache server, -disabling SSL, unless you have already setup SSL on your Apache server, -setting the correct username and password (the domain is ignored, you can -simply specify 'domain' or some other random string), and then going through -the standard activesync settings. - -Once you have done this, you should be able to synchronise your mobile -simply by clicking the 'Sync' button in ActiveSync on your mobile. - -*NOTE* using the synchronisation without SSL is not recommended because -your private data is transmitted in clear text over the net. Configuring -SSL on Apache is beyond of the scope of this document. Please refer to -Apache documention available at http://httpd.apache.org/docs/ - - -Troubleshooting ---------------- - -Most problems will be caused by incorrect Apache settings. To test whether -your Apache setup is working correctly, you can simply type the Z-Push URL -in your browser, to see if apache is correctly redirecting your request to -z-push. You can simply use: - - http:///Microsoft-Server-ActiveSync - -If correctly configured, you should see a username/password request and -when you specify a valid username and password, you should see a Z-Push -information page, saying that this kind of requests is not supported. -Without authentication credentials Z-Push displays general information. - -If not then check your PHP and Apache settings and Apache error logs. - -If you have other synchronisation problems, you can increase the LOGLEVEL -parameter in the config e.g. to LOGLEVEL_DEBUG or LOGLEVEL_WBXML. - -The z-push.log file will then collect detailed debug information from your -synchronisation. - -*NOTE* This setting will set Z-Push to log the detailed information for -*every* user on the system. You can set a different log level for particular -users by adding them comma separated to $specialLogUsers in the config.php - e.g. $specialLogUsers = array("user1", "user2", "user3"); - -*NOTE* Be aware that if you are using LOGLEVEL_DEBUG and LOGLEVEL_WBXML - Z-Push will be quite talkative, so it is advisable to use log-rotate - on the log file. - -*Repeated incorrect password messages* -If a password contains characters which are encoded differently in ISO-8859-1 -and Windows-1252 encodings (e.g. "§") the login might fail with Z-Push but -it works fine with the WebApp/Webaccess. The solution is to add: - -setlocale(LC_CTYPE, "en_US.UTF-8"); - -to the config.php file. diff --git a/sources/INSTALL_UPDATE_FROM_GIT b/sources/INSTALL_UPDATE_FROM_GIT deleted file mode 100644 index 01f3cd5..0000000 --- a/sources/INSTALL_UPDATE_FROM_GIT +++ /dev/null @@ -1,21 +0,0 @@ -Installing and updating from GIT can be time consuming, because we will have conflicts -with the config.php files. -Until upstream finds a good solution we can use this trick: - - -Setup - - Clone the repository to your web server - git checkout -b config to create the new config branch with the local configuration - -Updating - - Backup the local repository (or even better: also do this on a non-live copy) - git checkout master - git pull origin master to update the local master branch - git checkout config - git rebase master to rebase the offline config branch - Solve the conflicts (which will occur if the configuration files have been changed in the master) - - -Instructions provided by Martin Porcheron (@mporcheron) diff --git a/sources/LICENSE b/sources/LICENSE deleted file mode 100644 index 2ca933b..0000000 --- a/sources/LICENSE +++ /dev/null @@ -1,696 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. - - ---- - -Copyright 2007 - 2013 Zarafa Deutschland GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License, version 3, -as published by the Free Software Foundation with the following additional -term according to sec. 7: - -According to sec. 7 of the GNU Affero General Public License, version 3, -the terms of the AGPL are supplemented with the following terms: - -"Zarafa" is a registered trademark of Zarafa B.V. -"Z-Push" is a registered trademark of Zarafa Deutschland GmbH -The licensing of the Program under the AGPL does not imply a trademark license. -Therefore any rights, title and interest in our trademarks remain entirely with us. - -However, if you propagate an unmodified version of the Program you are -allowed to use the term "Z-Push" to indicate that you distribute the Program. -Furthermore you may use our trademarks where it is necessary to indicate -the intended purpose of a product or service provided you use it in accordance -with honest practices in industrial or commercial matters. -If you want to propagate modified versions of the Program under the name "Z-Push", -you may only do so if you have a written permission by Zarafa Deutschland GmbH -(to acquire a permission please contact Zarafa at trademark@zarafa.com). - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . \ No newline at end of file diff --git a/sources/NOTES b/sources/NOTES deleted file mode 100644 index cab93eb..0000000 --- a/sources/NOTES +++ /dev/null @@ -1,4 +0,0 @@ -Run composer to update autoinclude -================================== -curl -sS https://getcomposer.org/installer | php -php composer.phar dump-autoload -o diff --git a/sources/README.md b/sources/README.md deleted file mode 100644 index b10db1a..0000000 --- a/sources/README.md +++ /dev/null @@ -1,114 +0,0 @@ -Z-Push-contrib -============== - -[![Join the chat at https://gitter.im/fmbiete/Z-Push-contrib](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fmbiete/Z-Push-contrib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -This is a Z-Push fork with changes that I will try to put into the contrib branch, so they can get into the official Z-Push - -IMPORTANT: -For them to get into the official Z-Push, you must release the code under AGPLv3. Add this text to your commits "Released under the Affero GNU General Public License (AGPL) version 3" before merging. - -If you see some changes here, and you are the author, I will not contrib them, as I have no rights over them. -But I will try to reimplement with different code/approach so I can contribute them. When a sustitution is ready I will remove your changes from this repo. -If you want to help the community, contribute them yourself. - - -IMPORTANT 2: -All the code is AGPL licensed (or compatible, like the "include" files). So you can get a copy, modify it, use... your only obligation it's to publish your changes somewhere. - - ----------------------------------------------------- -Original Z-Push - -URL: http://www.zpush.org - -Z-Push is an implementation of the ActiveSync protocol, which is used 'over-the-air' for multi platform ActiveSync devices, including Windows Mobile, Ericsson and Nokia phones. With Z-Push any groupware can be connected and synced with these devices. - -License: GNU Affero Genaral Public License v3.0 (AGPLv3) - - -Documentation -============= -You can find some configuration guidelines in the Wiki https://github.com/fmbiete/Z-Push-contrib/wiki - -Requisites -========== -- PHP 5.x (5.3 it's the minimum supported) using PHP-FPM or MOD_PHP -- HHVM 3.6 or newer, instead of PHP -- NGINX or APACHE - -Configuration -============= - -NGINX, 1.4 at least or you will need to enable chunkin mode (Use google for Apache configuration) - - server { - listen 443; - server_name zpush.domain.com; - - ssl on; - ssl_certificate /etc/ssl/certs/zpush.pem; - ssl_certificate_key /etc/ssl/private/zpush.key; - - root /usr/share/www/z-push-contrib; - index index.php; - - error_log /var/log/nginx/zpush-error.log; - access_log /var/log/nginx/zpush-access.log; - - location / { - try_files $uri $uri/ index.php; - } - - location /Microsoft-Server-ActiveSync { - rewrite ^(.*)$ /index.php last; - } - - location ~ .php$ { - include /etc/nginx/fastcgi_params; - fastcgi_index index.php; - fastcgi_param HTTPS on; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_pass unix:/var/run/php5-fpm.sock; - # Z-Push Ping command will be alive for 470s, but be safe - fastcgi_read_timeout 630; - } - } - -PHP-FPM - - max_execution_time=600 - short_open_tag=On - -And configure enough php-fpm processes, as a rough estimation you will need 1.5 x number users. - - -Backends -======== -Each backend has a README file, or comments in their config.php file. Look at them to configure correctly. Also you can look here https://github.com/fmbiete/Z-Push-contrib/wiki - - -StateMachine -============ -You have 2 StateMachine methods. - -- FILE - FileStateMachine : will store state info into files. You will use it in Active-Pasive setups -- SQL - SqlStateMachine: will store state info into a database. It uses PHP-PDO, so you will need to install the required packages for your database flavour and create the database. You will use it in Active-Active setups. - - -User-Device Permissions -======================= -Disabled by default, when enabled will limit what users and device can sync against your Z-Push installation. -It can auto-accept users, users and device until a maximum number of devices is reached. - -If using with FileStateMachine, edit the file STATE_DIR/PreAuthUserDevices to modificate the behaivour. That file is JSON formatted and it's filled each time a new user connect. - -If using with SqlStateMachine, look at the zpush_preauth_users table. - - - - -Links -===== -Microsoft ActiveSync Specification -http://msdn.microsoft.com/en-us/library/cc425499%28v=exchg.80%29.aspx diff --git a/sources/autodiscover/INSTALL b/sources/autodiscover/INSTALL deleted file mode 100644 index 1128816..0000000 --- a/sources/autodiscover/INSTALL +++ /dev/null @@ -1,177 +0,0 @@ -Z-Push AutoDiscover manual --------------------------- -This manual gives an introduction to the Z-Push AutoDiscover service, discusses -technical details and explains the installation. - -Introduction ------------- -AutoDiscover is the service used to simplify the configuration of collaboration -accounts for clients, especially for mobile phones. -While in the past the user was required to enter the server name, user name and -password manually into his mobile phone in order to connect, with AutoDiscover -the user is only required to fill in his email address and the password. -AutoDiscover will try several methods to reach the correct server automatically. - - -How does it work? ------------------ -When speaking about AutoDiscover, this includes two distinct realms: -- AutoDiscover is a specification which defines the steps a client should take - in order to contact a service to request additional data. -- The AutoDiscover service is piece of software which accepts requests from the - clients, authenticates them, requests some additional data from the - collaboration server and sends this data back to the client. -The specification suggests several ways for client to contact the responsible -server to receive additional information. Tests have shown, that basically all -mobile phones tested support only the most basic ways. These are sufficient for -almost all types of scenarios and are the ones implemented by Z-Push AutoDiscover. -Please refer to the Mobile Compatibility List (http://z-push.sf.net/compatibility) -for an overview of supported and tested devices. -The used email address is the key for the process. The client splits it up into -the local and domain part (before and after the @-sign). The client then tries -to connect to this domain in order to get in contact with the AutoDiscover -service. The local part of the email address is used as "login" to the -AutoDiscover service. There is also an option, to use the full email address as -login name (see "Configuration" section below for details). - - - --------------- - | Client | - | e.g. mobile | - --------------- - / \ - 1. Searches for / \ 2. Data access - information / \ - / \ - V V - ---------------- -------------- - | AutoDiscover | redirects to | Z-Push | - | | --------------------> | ActiveSync | - ---------------- -------------- - \ / - Authen- \ / Synchronizes - ticates \ / - via Z-Push \ / - Backend V V - ----------------- - | Collaboration | - | Platform | - ----------------- - -Requirements ------------- -As described in the previous chapter, the local part of the email address or -the email address is used in order to log in. -Your configuration requires that this type of login is possible: -- either the user name is used to login and must be used in the email address - entered on the mobile, or -- the entire email address is used to login. - -Which option is used has to be configured in the AutoDiscover configuration and -in the underlying platform (e.g. ZCP (hosting mode)). -Most companies use the user name as local part of the email by default. From the -AutoDiscover point of view, it is not required that user is able to receive -emails at the used email address. It is recommended allowing that in order not -to confuse end users. - -AutoDiscover also requires a valid SSL certificate to work as expected. A very -little percentage of mobiles support self-signed certificates (showing a -pop-up alerting the user). Most mobiles silently ignore self-signed certificates -and just declare the AutoDiscover process as failed in such cases. -If AutoDiscover fails, the user is generally redirected to the -"manual configuration" of the client. - -If you do not plan to acquire an official certificate, you will probably not be -able to use the AutoDiscover service. -Depending on your setup, it could be necessary to add new DNS entries for your -mail domain. - - -Domain setup ------------- -There are two general ways the AutoDiscover process can be configured: -1. Directly with "yourdomain.com" website ("www.yourdomain.com" will most - probably not work) -2. With the sub-domain "autodiscover.yourdomain.com" -In both cases, an official SSL certificate is required. If you already have a -certificate for your domain, the webserver answering for that domain could be -reconfigured to allow AutoDiscover requests as well. In the case that you do -not have direct access to this type of configuration (e.g. hosting provider), -it's recommended to acquire a dedicated certificate for -"autodiscover.yourdomain.com". Please note, that this sub-domain can NOT be -renamed. In general, "wildcard" certificates can be used, as long they are -valid for the required domain. - - -Software requirements ---------------------- -Like Z-Push, AutoDiscover is written in PHP, where PHP 5.3 or newer is required. -Please consult the Z-Push INSTALL file for further information about PHP versions. -If only AutoDiscover is to be executed on a host, the Z-Push PHP dependencies do -NOT need to be installed. -AutoDiscover has one direct dependency, the php-xml parser library. - -These packages vary in names between the distributions. -- Generally install the packages: php-xml -- On Suse (SLES & OpenSuse) install the packages: php53-xml -- On RHEL based systems install the package: php-xml - - -Installation ------------- -AutoDiscover is part of the Z-Push package and uses some of the functionality -available in Z-Push. -It is possible to install AutoDiscover on the same host as Z-Push, or to -install them on different hosts. - -Currently, independently from the setup, it's recommended to extract the entire -z-push tarball and configure the services as required. -Please follow the install instructions from the Z-Push INSTALL file (section -"How to install") to copy the files to your server. -If you do not want to setup Z-Push on the host, do not add the "Alias" for -ActiveSync. - -To setup the SSL certificate, please refer to one of the many setup guides -available on the internet, like that one: -http://www.apache.com/resources/how-to-setup-an-ssl-certificate-on-apache/ - -The mobiles requests these URLs (where "yourdomain.com" corresponds to the -domain part of the email used in the client): - https://yourdomain.com/Autodiscover/Autodiscover.xml and/or - https://autodiscover.yourdomain.com/Autodiscover/Autodiscover.xml - -Add the following line to the apache site configuration file. - AliasMatch (?i)/Autodiscover/Autodiscover.xml$ "/usr/share/z-push/autodiscover/autodiscover.php" - -This line assumes that Z-Push is installed in /usr/share/z-push. If the path -is different, please adjust it accordingly. - -Note: some mobiles use different casings, like "AutoDiscover" in the URL. The -above statement is valid for these as well. - -Please restart Apache afterwards. - - -Configuration -------------- -You don't need extra configuration for the AutoDiscover Service. It will use -the configuration already defined for the main Z-Push Service. - - -Test installation ------------------ -If everything is correct, accessing with a browser the URL for your setup, you -should see: - 1. a pop-up asking for your username + password. Always use the email - address which you would also enter on the mobile (independently from - the configuration). - 2. if the authentication was successful, you will see a Z-Push informational - page (like when accessing the Z-Push location). - -Note: The same test can also be performed in the mobiles web browser to check -if the access works correctly from the mobile network. - -If the authentication fails, please check the configuration options of AutoDiscover. -Also check the logfiles for possible failures. - -If the manual method works, try setting up your mobile phone! :) diff --git a/sources/autodiscover/autodiscover.php b/sources/autodiscover/autodiscover.php deleted file mode 100644 index a22cb71..0000000 --- a/sources/autodiscover/autodiscover.php +++ /dev/null @@ -1,256 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -require_once '../vendor/autoload.php'; -require_once '../config.php'; - -class ZPushAutodiscover { - const ACCEPTABLERESPONSESCHEMA = 'http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006'; - const MAXINPUTSIZE = 8192; // Bytes, the autodiscover request shouldn't exceed that value - - private static $instance; - - /** - * Static method to start the autodiscover process. - * - * @access public - * - * @return void - */ - public static function DoZPushAutodiscover() { - ZLog::Write(LOGLEVEL_DEBUG, '-------- Start ZPushAutodiscover'); - // TODO use filterevilinput? - if (stripos($_SERVER["REQUEST_METHOD"], "GET") !== false) { - ZLog::Write(LOGLEVEL_WARN, "GET request for autodiscover. Exiting."); - if (!headers_sent()) { - ZPush::PrintZPushLegal('GET not supported'); - } - ZLog::Write(LOGLEVEL_DEBUG, '-------- End ZPushAutodiscover'); - exit(1); - } - if (!isset(self::$instance)) { - self::$instance = new ZPushAutodiscover(); - } - self::$instance->DoAutodiscover(); - ZLog::Write(LOGLEVEL_DEBUG, '-------- End ZPushAutodiscover'); - } - - /** - * Does the complete autodiscover. - * @access public - * @throws AuthenticationRequiredException if login to the backend failed. - * @throws ZPushException if the incoming XML is invalid.. - * - * @return void - */ - public function DoAutodiscover() { - if (!defined('REAL_BASE_PATH')) { - define('REAL_BASE_PATH', str_replace('autodiscover/', '', BASE_PATH)); - } - set_include_path(get_include_path() . PATH_SEPARATOR . REAL_BASE_PATH); - $response = ""; - - try { - $incomingXml = $this->getIncomingXml(); - $backend = ZPush::GetBackend(); - $username = $this->login($backend, $incomingXml); - $userDetails = $backend->GetUserDetails($username); - $email = ($this->getAttribFromUserDetails($userDetails, 'emailaddress')) ? $this->getAttribFromUserDetails($userDetails, 'emailaddress') : $incomingXml->Request->EMailAddress; - $userFullname = ($this->getAttribFromUserDetails($userDetails, 'fullname')) ? $this->getAttribFromUserDetails($userDetails, 'fullname') : $email; - ZLog::Write(LOGLEVEL_WBXML, sprintf("Resolved user's '%s' fullname to '%s'", $username, $userFullname)); - $response = $this->createResponse($email, $userFullname); - setcookie("membername", $username); - } - - catch (AuthenticationRequiredException $ex) { - if (isset($incomingXml)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because login failed for user with email '%s'", $incomingXml->Request->EMailAddress)); - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover incorrect request: '%s'", $ex->getMessage())); - } - http_response_code(401); - header('WWW-Authenticate: Basic realm="ZPush"'); - } - catch (ZPushException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to complete autodiscover because of ZPushException. Error: %s", $ex->getMessage())); - if(!headers_sent()) { - header('HTTP/1.1 '. $ex->getHTTPCodeString()); - foreach ($ex->getHTTPHeaders() as $h) { - header($h); - } - } - } - $this->sendResponse($response); - } - - /** - * Processes the incoming XML request and parses it to a SimpleXMLElement. - * - * @access private - * @throws ZPushException if the XML is invalid. - * @throws AuthenticationRequiredException if no login data was sent. - * - * @return SimpleXMLElement - */ - private function getIncomingXml() { - if ($_SERVER['CONTENT_LENGTH'] > ZPushAutodiscover::MAXINPUTSIZE) { - throw new ZPushException('The request input size exceeds 8kb.'); - } - - if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) { - throw new AuthenticationRequiredException(); - } - - $input = @file_get_contents('php://input'); - $xml = simplexml_load_string($input); - - if (LOGLEVEL >= LOGLEVEL_WBXML) { - ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->getIncomingXml() incoming XML data:%s%s", PHP_EOL, $xml->asXML())); - } - - if (!isset($xml->Request->EMailAddress)) { - throw new FatalException('Invalid input XML: no email address.'); - } - - if (Utils::GetLocalPartFromEmail($xml->Request->EMailAddress) != Utils::GetLocalPartFromEmail($_SERVER['PHP_AUTH_USER'])) { - ZLog::Write(LOGLEVEL_WARN, sprintf("The local part of the server auth user is different from the local part in the XML request ('%s' != '%s')", - Utils::GetLocalPartFromEmail($xml->Request->EMailAddress), Utils::GetLocalPartFromEmail($_SERVER['PHP_AUTH_USER']))); - } - - if (!isset($xml->Request->AcceptableResponseSchema)) { - throw new FatalException('Invalid input XML: no AcceptableResponseSchema.'); - } - - if ($xml->Request->AcceptableResponseSchema != ZPushAutodiscover::ACCEPTABLERESPONSESCHEMA) { - throw new FatalException('Invalid input XML: not a mobilesync responseschema.'); - } - - return $xml; - } - - /** - * Logins using the backend's Logon function. - * - * @param IBackend $backend - * @param String $incomingXml - * @access private - * @throws AuthenticationRequiredException if no login data was sent. - * - * @return string $username - */ - private function login($backend, $incomingXml) { - // Determine the login name depending on the configuration: complete email address or - // the local part only. - if (USE_FULLEMAIL_FOR_LOGIN) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the complete email address for login.")); - $username = $incomingXml->Request->EMailAddress; - } - else{ - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the username only for login.")); - $username = Utils::GetLocalPartFromEmail($incomingXml->Request->EMailAddress); - } - - if($backend->Logon($username, "", $_SERVER['PHP_AUTH_PW']) == false) { - throw new AuthenticationRequiredException("Access denied. Username or password incorrect."); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login() Using '%s' as the username.", $username)); - return $username; - } - - /** - * Creates the XML response. - * - * @param string $email - * @param string $userFullname - * @access private - * - * @return string - */ - private function createResponse($email, $userFullname) { - $server_url = 'https://' . $_SERVER['SERVER_NAME'] . '/Microsoft-Server-ActiveSync'; - $xml = file_get_contents('response.xml'); - $response = new SimpleXMLElement($xml); - $response->Response->User->DisplayName = $userFullname; - $response->Response->User->EMailAddress = $email; - $response->Response->Action->Settings->Server->Url = $server_url; - $response->Response->Action->Settings->Server->Name = $server_url; - $response = $response->asXML(); - ZLog::Write(LOGLEVEL_WBXML, sprintf("ZPushAutodiscover->createResponse() XML response:%s%s", PHP_EOL, $response)); - return $response; - } - - /** - * Sends the response to the device. - * @param string $response - * @access private - * - * @return void - */ - private function sendResponse($response) { - ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() sending response..."); - header("Content-type: text/html"); - $output = fopen("php://output", "w+"); - fwrite($output, $response); - fclose($output); - ZLog::Write(LOGLEVEL_DEBUG, "ZPushAutodiscover->sendResponse() response sent."); - } - - /** - * Gets an attribute from user details. - * @param Array $userDetails - * @param String $attrib - * @access private - * - * @return String or false on error. - */ - private function getAttribFromUserDetails($userDetails, $attrib) { - if (isset($userDetails[$attrib]) && $userDetails[$attrib]) { - return $userDetails[$attrib]; - } - ZLog::Write(LOGLEVEL_WARN, sprintf("The backend was not able to find attribute '%s' of the user. Fall back to the default value.", $attrib)); - return false; - } -} - -ZPushAutodiscover::DoZPushAutodiscover(); diff --git a/sources/autodiscover/response.xml b/sources/autodiscover/response.xml deleted file mode 100644 index 9fa473d..0000000 --- a/sources/autodiscover/response.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - -en:us - - - - - - - -MobileSync - - - - - - - \ No newline at end of file diff --git a/sources/backend/caldav/AUTHOR b/sources/backend/caldav/AUTHOR deleted file mode 100644 index af8002c..0000000 --- a/sources/backend/caldav/AUTHOR +++ /dev/null @@ -1,3 +0,0 @@ -The Author of this backend is dupondje, I could have modified it. -You can found the original code here: -https://github.com/dupondje/PHP-Push-2 diff --git a/sources/backend/caldav/REQUIREMENTS b/sources/backend/caldav/REQUIREMENTS deleted file mode 100644 index cd00f71..0000000 --- a/sources/backend/caldav/REQUIREMENTS +++ /dev/null @@ -1,8 +0,0 @@ -REQUIREMENTS: -php-curl -libawl-php - -INSTALL: -Add your awl folder to the include_path variable (/etc/php.ini or similar) - -CalDAV server (DAViCal, Sabredav, Sogo, Owncloud...) diff --git a/sources/backend/caldav/caldav.php b/sources/backend/caldav/caldav.php deleted file mode 100644 index 811c798..0000000 --- a/sources/backend/caldav/caldav.php +++ /dev/null @@ -1,1528 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/caldav/config.php"); - -class BackendCalDAV extends BackendDiff { - /** - * @var CalDAVClient - */ - private $_caldav; - private $_caldav_path; - private $_collection = array(); - - private $changessinkinit; - private $sinkdata; - private $sinkmax; - - /** - * Constructor - */ - public function BackendCalDAV() { - if (!function_exists("curl_init")) { - throw new FatalException("BackendCalDAV(): php-curl is not found", 0, null, LOGLEVEL_FATAL); - } - - $this->changessinkinit = false; - $this->sinkdata = array(); - $this->sinkmax = array(); - } - - /** - * Login to the CalDAV backend - * @see IBackend::Logon() - */ - public function Logon($username, $domain, $password) { - $this->_caldav_path = str_replace('%u', $username, CALDAV_PATH); - $url = sprintf("%s://%s:%d%s", CALDAV_PROTOCOL, CALDAV_SERVER, CALDAV_PORT, $this->_caldav_path); - $this->_caldav = new CalDAVClient($url, $username, $password); - if ($connected = $this->_caldav->CheckConnection()) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->Logon(): User '%s' is authenticated on CalDAV '%s'", $username, $url)); - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCalDAV->Logon(): User '%s' is not authenticated on CalDAV '%s'", $username, $url)); - } - - return $connected; - } - - /** - * The connections to CalDAV are always directly closed. So nothing special needs to happen here. - * @see IBackend::Logoff() - */ - public function Logoff() { - if ($this->_caldav != null) { - $this->_caldav->Disconnect(); - unset($this->_caldav); - } - - $this->SaveStorages(); - - unset($this->sinkdata); - unset($this->sinkmax); - - ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->Logoff(): disconnected from CALDAV server"); - - return true; - } - - /** - * CalDAV doesn't need to handle SendMail - * @see IBackend::SendMail() - */ - public function SendMail($sm) { - return false; - } - - /** - * No attachments in CalDAV - * @see IBackend::GetAttachmentData() - */ - public function GetAttachmentData($attname) { - return false; - } - - /** - * Deletes are always permanent deletes. Messages doesn't get moved. - * @see IBackend::GetWasteBasket() - */ - public function GetWasteBasket() { - return false; - } - - /** - * Get a list of all the folders we are going to sync. - * Each caldav calendar can contain tasks (prefix T) and events (prefix C), so duplicate each calendar found. - * @see BackendDiff::GetFolderList() - */ - public function GetFolderList() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetFolderList(): Getting all folders.")); - $folders = array(); - $calendars = $this->_caldav->FindCalendars(); - foreach ($calendars as $val) { - $folder = array(); - $fpath = explode("/", $val->url, -1); - if (is_array($fpath)) { - $folderid = array_pop($fpath); - $id = "C" . $folderid; - $folders[] = $this->StatFolder($id); - $id = "T" . $folderid; - $folders[] = $this->StatFolder($id); - } - } - return $folders; - } - - /** - * Returning a SyncFolder - * @see BackendDiff::GetFolder() - */ - public function GetFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetFolder('%s')", $id)); - $val = $this->_caldav->GetCalendarDetails($this->_caldav_path . substr($id, 1) . "/"); - $folder = new SyncFolder(); - $folder->parentid = "0"; - $folder->displayname = $val->displayname; - $folder->serverid = $id; - if ($id[0] == "C") { - if (defined('CALDAV_PERSONAL') && strtolower(substr($id, 1)) == CALDAV_PERSONAL) { - $folder->type = SYNC_FOLDER_TYPE_APPOINTMENT; - } - else { - $folder->type = SYNC_FOLDER_TYPE_USER_APPOINTMENT; - } - } - else { - if (defined('CALDAV_PERSONAL') && strtolower(substr($id, 1)) == CALDAV_PERSONAL) { - $folder->type = SYNC_FOLDER_TYPE_TASK; - } - else { - $folder->type = SYNC_FOLDER_TYPE_USER_TASK; - } - } - return $folder; - } - - /** - * Returns information on the folder. - * @see BackendDiff::StatFolder() - */ - public function StatFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->StatFolder('%s')", $id)); - $val = $this->GetFolder($id); - $folder = array(); - $folder["id"] = $id; - $folder["parent"] = $val->parentid; - $folder["mod"] = $val->serverid; - return $folder; - } - - /** - * ChangeFolder is not supported under CalDAV - * @see BackendDiff::ChangeFolder() - */ - public function ChangeFolder($folderid, $oldid, $displayname, $type) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangeFolder('%s','%s','%s','%s')", $folderid, $oldid, $displayname, $type)); - return false; - } - - /** - * DeleteFolder is not supported under CalDAV - * @see BackendDiff::DeleteFolder() - */ - public function DeleteFolder($id, $parentid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->DeleteFolder('%s','%s')", $id, $parentid)); - return false; - } - - /** - * Get a list of all the messages. - * @see BackendDiff::GetMessageList() - */ - public function GetMessageList($folderid, $cutoffdate) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetMessageList('%s','%s')", $folderid, $cutoffdate)); - - /* Calculating the range of events we want to sync */ - $begin = gmdate("Ymd\THis\Z", $cutoffdate); - $finish = gmdate("Ymd\THis\Z", CALDAV_MAX_SYNC_PERIOD); - - $path = $this->_caldav_path . substr($folderid, 1) . "/"; - if ($folderid[0] == "C") { - $msgs = $this->_caldav->GetEvents($begin, $finish, $path); - } - else { - $msgs = $this->_caldav->GetTodos($begin, $finish, false, false, $path); - } - - $messages = array(); - foreach ($msgs as $e) { - $id = $e['href']; - $this->_collection[$id] = $e; - $messages[] = $this->StatMessage($folderid, $id); - } - return $messages; - } - - /** - * Get a SyncObject by its ID - * @see BackendDiff::GetMessage() - */ - public function GetMessage($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->GetMessage('%s','%s')", $folderid, $id)); - $data = $this->_collection[$id]['data']; - - if ($folderid[0] == "C") { - return $this->_ParseVEventToAS($data, $contentparameters); - } - if ($folderid[0] == "T") { - return $this->_ParseVTodoToAS($data, $contentparameters); - } - return false; - } - - /** - * Return id, flags and mod of a messageid - * @see BackendDiff::StatMessage() - */ - public function StatMessage($folderid, $id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->StatMessage('%s','%s')", $folderid, $id)); - $type = "VEVENT"; - if ($folderid[0] == "T") { - $type = "VTODO"; - } - $data = null; - if (array_key_exists($id, $this->_collection)) { - $data = $this->_collection[$id]; - } - else { - $path = $this->_caldav_path . substr($folderid, 1) . "/"; - $e = $this->_caldav->GetEntryByUid(substr($id, 0, strlen($id)-4), $path, $type); - if ($e == null && count($e) <= 0) - return; - $data = $e[0]; - } - $message = array(); - $message['id'] = $data['href']; - $message['flags'] = "1"; - $message['mod'] = $data['etag']; - return $message; - } - - /** - * Change/Add a message with contents received from ActiveSync - * @see BackendDiff::ChangeMessage() - */ - public function ChangeMessage($folderid, $id, $message, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangeMessage('%s','%s')", $folderid, $id)); - - if ($id) { - $mod = $this->StatMessage($folderid, $id); - $etag = $mod['mod']; - } - else { - $etag = "*"; - $id = sprintf("%s-%s.ics", gmdate("Ymd\THis\Z"), hash("md5", microtime())); - } - - $url = $this->_caldav_path . substr($folderid, 1) . "/" . $id; - - $data = $this->_ParseASToVCalendar($message, $folderid, substr($id, 0, strlen($id) - 4)); - - $etag_new = $this->CreateUpdateCalendar($data, $url, $etag); - - $item = array(); - $item['href'] = $id; - $item['etag'] = $etag_new; - $item['data'] = $data; - $this->_collection[$id] = $item; - - return $this->StatMessage($folderid, $id); - } - - /** - * Change the read flag is not supported. - * @see BackendDiff::SetReadFlag() - */ - public function SetReadFlag($folderid, $id, $flags, $contentParameters) { - return false; - } - - /** - * Delete a message from the CalDAV server. - * @see BackendDiff::DeleteMessage() - */ - public function DeleteMessage($folderid, $id, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->DeleteMessage('%s','%s')", $folderid, $id)); - $url = $this->_caldav_path . substr($folderid, 1) . "/" . $id; - $http_status_code = $this->_caldav->DoDELETERequest($url); - return $http_status_code == "204"; - } - - /** - * Move a message is not supported by CalDAV. - * @see BackendDiff::MoveMessage() - */ - public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) { - return false; - } - - /** - * Create or Update one event - * - * @access public - * @param $data string VCALENDAR text - * @param $url string URL for the calendar, if false a new calendar object is created - * @param $etag string ETAG for the calendar, if '*' is a new object - * @return array - */ - public function CreateUpdateCalendar($data, $url = false, $etag = "*") { - if ($url === false) { - $url = sprintf("%s%s/%s-%s.ics", $this->_caldav_path, CALDAV_PERSONAL, gmdate("Ymd\THis\Z"), hash("md5", microtime())); - $etag = "*"; - } - - return $this->_caldav->DoPUTRequest($url, $data, $etag); - } - - /** - * Deletes one VCALENDAR - * - * @access public - * @param $id string ID of the VCALENDAR - * @return boolean - */ - public function DeleteCalendar($id) { - $http_status_code = $this->_caldav->DoDELETERequest(sprintf("%s%s/%s", $this->_caldav_path, CALDAV_PERSONAL, $id)); - return $http_status_code == "204"; - } - - /** - * Finds one VCALENDAR - * - * @access public - * @param $uid string UID attribute - * @return array - */ - public function FindCalendar($uid) { - $filter = sprintf("%s", $uid); - - $events = $this->_caldav->DoCalendarQuery($filter, sprintf("%s%s", $this->_caldav_path, CALDAV_PERSONAL)); - - return $events; - } - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - // TODO: - return false; - } - - /** - * Indicates which AS version is supported by the backend. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_14; - } - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * The CalDAV backend simulates a sink by polling revision dates from the events or use the native sync-collection. - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - return true; - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBackend->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if found can not be found - */ - public function ChangesSinkInitialize($folderid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSinkInitialize(): folderid '%s'", $folderid)); - - // We don't need the actual events, we only need to get the changes since this moment - $init_ok = true; - $url = $this->_caldav_path . substr($folderid, 1) . "/"; - $this->sinkdata[$folderid] = $this->_caldav->GetSync($url, true, CALDAV_SUPPORTS_SYNC); - if (CALDAV_SUPPORTS_SYNC) { - // we don't need to store the sinkdata if the caldav server supports native sync - unset($this->sinkdata[$url]); - $this->sinkdata[$folderid] = array(); - } - - $this->changessinkinit = $init_ok; - $this->sinkmax = array(); - - return $this->changessinkinit; - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - $notifications = array(); - $stopat = time() + $timeout - 1; - - //We can get here and the ChangesSink not be initialized yet - if (!$this->changessinkinit) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Not initialized ChangesSink, sleep and exit")); - // We sleep and do nothing else - sleep($timeout); - return $notifications; - } - - // only check once to reduce pressure in the DAV server - foreach ($this->sinkdata as $k => $v) { - $changed = false; - - $url = $this->_caldav_path . substr($k, 1) . "/"; - $response = $this->_caldav->GetSync($url, false, CALDAV_SUPPORTS_SYNC); - - if (CALDAV_SUPPORTS_SYNC) { - if (count($response) > 0) { - $changed = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected")); - } - } - else { - // If the numbers of events are different, we know for sure, there are changes - if (count($response) != count($v)) { - $changed = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected")); - } - else { - // If the numbers of events are equals, we compare the biggest date - // FIXME: we are comparing strings no dates - if (!isset($this->sinkmax[$k])) { - $this->sinkmax[$k] = ''; - for ($i = 0; $i < count($v); $i++) { - if ($v[$i]['getlastmodified'] > $this->sinkmax[$k]) { - $this->sinkmax[$k] = $v[$i]['getlastmodified']; - } - } - } - - for ($i = 0; $i < count($response); $i++) { - if ($response[$i]['getlastmodified'] > $this->sinkmax[$k]) { - $changed = true; - } - } - - if ($changed) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->ChangesSink - Changes detected")); - } - } - } - - if ($changed) { - $notifications[] = $k; - } - } - - // Wait to timeout - if (empty($notifications)) { - while ($stopat > time()) { - sleep(1); - } - } - - return $notifications; - } - - - /** - * Convert a iCAL VEvent to ActiveSync format - * @param ical_vevent $data - * @param ContentParameters $contentparameters - * @return SyncAppointment - */ - private function _ParseVEventToAS($data, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendCalDAV->_ParseVEventToAS(): Parsing VEvent"); - - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - $message = new SyncAppointment(); - - $ical = new iCalComponent($data); - $timezones = $ical->GetComponents("VTIMEZONE"); - $timezone = ""; - if (count($timezones) > 0) { - $timezone = Utils::ParseTimezone($timezones[0]->GetPValue("TZID")); - } - if (!$timezone) { - $timezone = date_default_timezone_get(); - } - $message->timezone = $this->_GetTimezoneString($timezone); - - $vevents = $ical->GetComponents("VTIMEZONE", false); - foreach ($vevents as $event) { - $rec = $event->GetProperties("RECURRENCE-ID"); - if (count($rec) > 0) { - $recurrence_id = reset($rec); - $exception = new SyncAppointmentException(); - $tzid = Utils::ParseTimezone($recurrence_id->GetParameterValue("TZID")); - if (!$tzid) { - $tzid = $timezone; - } - $exception->exceptionstarttime = Utils::MakeUTCDate($recurrence_id->Value(), $tzid); - $exception->deleted = "0"; - $exception = $this->_ParseVEventToSyncObject($event, $exception, $truncsize); - if (!isset($message->exceptions)) { - $message->exceptions = array(); - } - $message->exceptions[] = $exception; - } - else { - $message = $this->_ParseVEventToSyncObject($event, $message, $truncsize); - } - } - return $message; - } - - /** - * Parse 1 VEvent - * @param ical_vevent $event - * @param SyncAppointment(Exception) $message - * @param int $truncsize - */ - private function _ParseVEventToSyncObject($event, $message, $truncsize) { - //Defaults - $message->busystatus = "2"; - - $properties = $event->GetProperties(); - foreach ($properties as $property) { - switch ($property->Name()) { - case "LAST-MODIFIED": - $message->dtstamp = Utils::MakeUTCDate($property->Value()); - break; - - case "DTSTART": - $message->starttime = Utils::MakeUTCDate($property->Value(), Utils::ParseTimezone($property->GetParameterValue("TZID"))); - if (strlen($property->Value()) == 8) { - $message->alldayevent = "1"; - } - break; - - case "SUMMARY": - $message->subject = $property->Value(); - break; - - case "UID": - $message->uid = $property->Value(); - break; - - case "ORGANIZER": - $org_mail = str_ireplace("MAILTO:", "", $property->Value()); - $message->organizeremail = $org_mail; - $org_cn = $property->GetParameterValue("CN"); - if ($org_cn) { - $message->organizername = $org_cn; - } - break; - - case "LOCATION": - $message->location = $property->Value(); - break; - - case "DTEND": - $message->endtime = Utils::MakeUTCDate($property->Value(), Utils::ParseTimezone($property->GetParameterValue("TZID"))); - if (strlen($property->Value()) == 8) { - $message->alldayevent = "1"; - } - break; - - case "DURATION": - if (!isset($message->endtime)) { - $start = date_create("@" . $message->starttime); - $val = str_replace("+", "", $property->Value()); - $interval = new DateInterval($val); - $message->endtime = date_timestamp_get(date_add($start, $interval)); - } - break; - - case "RRULE": - $message->recurrence = $this->_ParseRecurrence($property->Value(), "vevent"); - break; - - case "CLASS": - switch ($property->Value()) { - case "PUBLIC": - $message->sensitivity = "0"; - break; - case "PRIVATE": - $message->sensitivity = "2"; - break; - case "CONFIDENTIAL": - $message->sensitivity = "3"; - break; - } - break; - - case "TRANSP": - switch ($property->Value()) { - case "TRANSPARENT": - $message->busystatus = "0"; - break; - case "OPAQUE": - $message->busystatus = "2"; - break; - } - break; - - // SYNC_POOMCAL_MEETINGSTATUS - // Meetingstatus values - // 0 = is not a meeting - // 1 = is a meeting - // 3 = Meeting received - // 5 = Meeting is canceled - // 7 = Meeting is canceled and received - // 9 = as 1 - // 11 = as 3 - // 13 = as 5 - // 15 = as 7 - case "STATUS": - switch ($property->Value()) { - case "TENTATIVE": - $message->meetingstatus = "3"; // was 1 - break; - case "CONFIRMED": - $message->meetingstatus = "1"; // was 3 - break; - case "CANCELLED": - $message->meetingstatus = "5"; // could also be 7 - break; - } - break; - - case "ATTENDEE": - $attendee = new SyncAttendee(); - $att_email = str_ireplace("MAILTO:", "", $property->Value()); - $attendee->email = $att_email; - $att_cn = $property->GetParameterValue("CN"); - if ($att_cn) { - $attendee->name = $att_cn; - } - if (isset($message->attendees) && is_array($message->attendees)) { - $message->attendees[] = $attendee; - } - else { - $message->attendees = array($attendee); - } - break; - - case "DESCRIPTION": - if (Request::GetProtocolVersion() >= 12.0) { - $message->asbody = new SyncBaseBody(); - $message->asbody->data = str_replace("\n","\r\n", str_replace("\r","",Utils::ConvertHtmlToText($property->Value()))); - // truncate body, if requested - if (strlen($message->asbody->data) > $truncsize) { - $message->asbody->truncated = 1; - $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $truncsize); - } - else { - $message->asbody->truncated = 0; - } - $message->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; - } - else { - $body = $property->Value(); - // truncate body, if requested - if(strlen($body) > $truncsize) { - $body = Utils::Utf8_truncate($body, $truncsize); - $message->bodytruncated = 1; - } else { - $message->bodytruncated = 0; - } - $body = str_replace("\n","\r\n", str_replace("\r","",$body)); - $message->body = $body; - } - break; - - case "CATEGORIES": - $categories = explode(",", $property->Value()); - $message->categories = $categories; - break; - - case "EXDATE": - $exception = new SyncAppointmentException(); - $exception->deleted = "1"; - $exception->exceptionstarttime = Utils::MakeUTCDate($property->Value()); - if (!isset($message->exceptions)) { - $message->exceptions = array(); - } - $message->exceptions[] = $exception; - break; - - //We can ignore the following - case "PRIORITY": - case "SEQUENCE": - case "CREATED": - case "DTSTAMP": - case "X-MOZ-GENERATION": - case "X-MOZ-LASTACK": - case "X-LIC-ERROR": - case "RECURRENCE-ID": - break; - - default: - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): '%s' is not yet supported.", $property->Name())); - } - } - - // Workaround #127 - No organizeremail defined - if (!isset($message->organizeremail)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVEventToSyncObject(): No organizeremail defined, using username")); - $message->organizeremail = $this->originalUsername; - } - - $valarm = current($event->GetComponents("VALARM")); - if ($valarm) { - $properties = $valarm->GetProperties(); - foreach ($properties as $property) { - if ($property->Name() == "TRIGGER") { - $parameters = $property->Parameters(); - if (array_key_exists("VALUE", $parameters) && $parameters["VALUE"] == "DATE-TIME") { - $trigger = date_create("@" . Utils::MakeUTCDate($property->Value())); - $begin = date_create("@" . $message->starttime); - $interval = date_diff($begin, $trigger); - $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24; - } - elseif (!array_key_exists("VALUE", $parameters) || $parameters["VALUE"] == "DURATION") { - $val = str_replace("-", "", $property->Value()); - $interval = new DateInterval($val); - $message->reminder = $interval->format("%i") + $interval->format("%h") * 60 + $interval->format("%a") * 60 * 24; - } - } - } - } - - return $message; - } - - /** - * Parse a RRULE - * @param string $rrulestr - */ - private function _ParseRecurrence($rrulestr, $type) { - $recurrence = new SyncRecurrence(); - if ($type == "vtodo") { - $recurrence = new SyncTaskRecurrence(); - } - $rrules = explode(";", $rrulestr); - foreach ($rrules as $rrule) { - $rule = explode("=", $rrule); - switch ($rule[0]) { - case "FREQ": - switch ($rule[1]) { - case "DAILY": - $recurrence->type = "0"; - break; - case "WEEKLY": - $recurrence->type = "1"; - break; - case "MONTHLY": - $recurrence->type = "2"; - break; - case "YEARLY": - $recurrence->type = "5"; - } - break; - - case "UNTIL": - $recurrence->until = Utils::MakeUTCDate($rule[1]); - break; - - case "COUNT": - $recurrence->occurrences = $rule[1]; - break; - - case "INTERVAL": - $recurrence->interval = $rule[1]; - break; - - case "BYDAY": - $dval = 0; - $days = explode(",", $rule[1]); - foreach ($days as $day) { - if ($recurrence->type == "2") { - if (strlen($day) > 2) { - $recurrence->weekofmonth = intval($day); - $day = substr($day,-2); - } - else { - $recurrence->weekofmonth = 1; - } - $recurrence->type = "3"; - } - switch ($day) { - // 1 = Sunday - // 2 = Monday - // 4 = Tuesday - // 8 = Wednesday - // 16 = Thursday - // 32 = Friday - // 62 = Weekdays // not in spec: daily weekday recurrence - // 64 = Saturday - case "SU": - $dval += 1; - break; - case "MO": - $dval += 2; - break; - case "TU": - $dval += 4; - break; - case "WE": - $dval += 8; - break; - case "TH": - $dval += 16; - break; - case "FR": - $dval += 32; - break; - case "SA": - $dval += 64; - break; - } - } - $recurrence->dayofweek = $dval; - break; - - //Only 1 BYMONTHDAY is supported, so BYMONTHDAY=2,3 will only include 2 - case "BYMONTHDAY": - $days = explode(",", $rule[1]); - $recurrence->dayofmonth = $days[0]; - break; - - case "BYMONTH": - $recurrence->monthofyear = $rule[1]; - break; - - default: - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseRecurrence(): '%s' is not yet supported.", $rule[0])); - } - } - return $recurrence; - } - - /** - * Generate a iCAL VCalendar from ActiveSync object. - * @param string $data - * @param string $folderid - * @param string $id - */ - private function _ParseASToVCalendar($data, $folderid, $id) { - $ical = new iCalComponent(); - $ical->SetType("VCALENDAR"); - $ical->AddProperty("VERSION", "2.0"); - $ical->AddProperty("PRODID", "-//z-push-contrib//NONSGML Z-Push-contrib Calendar//EN"); - $ical->AddProperty("CALSCALE", "GREGORIAN"); - - if ($folderid[0] == "C") { - $vevent = $this->_ParseASEventToVEvent($data, $id); - $vevent->AddProperty("UID", $id); - $ical->AddComponent($vevent); - if (isset($data->exceptions) && is_array($data->exceptions)) { - foreach ($data->exceptions as $ex) { - $exception = $this->_ParseASEventToVEvent($ex, $id); - if ($data->alldayevent == 1) { - $exception->AddProperty("RECURRENCE-ID", $this->_GetDateFromUTC("Ymd", $ex->exceptionstarttime, $data->timezone), array("VALUE" => "DATE")); - } - else { - $exception->AddProperty("RECURRENCE-ID", gmdate("Ymd\THis\Z", $ex->exceptionstarttime)); - } - $exception->AddProperty("UID", $id); - $ical->AddComponent($exception); - } - } - } - if ($folderid[0] == "T") { - $vtodo = $this->_ParseASTaskToVTodo($data, $id); - $vtodo->AddProperty("UID", $id); - $vtodo->AddProperty("DTSTAMP", gmdate("Ymd\THis\Z")); - $ical->AddComponent($vtodo); - } - - return $ical->Render(); - } - - /** - * Generate a VEVENT from a SyncAppointment(Exception). - * @param string $data - * @param string $id - * @return iCalComponent - */ - private function _ParseASEventToVEvent($data, $id) { - $vevent = new iCalComponent(); - $vevent->SetType("VEVENT"); - - if (isset($data->dtstamp)) { - $vevent->AddProperty("DTSTAMP", gmdate("Ymd\THis\Z", $data->dtstamp)); - $vevent->AddProperty("LAST-MODIFIED", gmdate("Ymd\THis\Z", $data->dtstamp)); - } - if (isset($data->starttime)) { - if ($data->alldayevent == 1) { - $vevent->AddProperty("DTSTART", $this->_GetDateFromUTC("Ymd", $data->starttime, $data->timezone), array("VALUE" => "DATE")); - } - else { - $vevent->AddProperty("DTSTART", gmdate("Ymd\THis\Z", $data->starttime)); - } - } - if (isset($data->subject)) { - $vevent->AddProperty("SUMMARY", $data->subject); - } - if (isset($data->organizeremail)) { - if (isset($data->organizername)) { - $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $data->organizeremail), array("CN" => $data->organizername)); - } - else { - $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $data->organizeremail)); - } - } - if (isset($data->location)) { - $vevent->AddProperty("LOCATION", $data->location); - } - if (isset($data->endtime)) { - if ($data->alldayevent == 1) { - $vevent->AddProperty("DTEND", $this->_GetDateFromUTC("Ymd", $data->endtime, $data->timezone), array("VALUE" => "DATE")); - $vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"); - } - else { - $vevent->AddProperty("DTEND", gmdate("Ymd\THis\Z", $data->endtime)); - $vevent->AddProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "FALSE"); - } - } - if (isset($data->recurrence)) { - $vevent->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence)); - } - if (isset($data->sensitivity)) { - switch ($data->sensitivity) { - case "0": - $vevent->AddProperty("CLASS", "PUBLIC"); - break; - case "2": - $vevent->AddProperty("CLASS", "PRIVATE"); - break; - case "3": - $vevent->AddProperty("CLASS", "CONFIDENTIAL"); - break; - } - } - if (isset($data->busystatus)) { - switch ($data->busystatus) { - case "0": - case "1": - $vevent->AddProperty("TRANSP", "TRANSPARENT"); - break; - case "2": - case "3": - $vevent->AddProperty("TRANSP", "OPAQUE"); - break; - } - } - if (isset($data->reminder)) { - $valarm = new iCalComponent(); - $valarm->SetType("VALARM"); - $valarm->AddProperty("ACTION", "DISPLAY"); - $trigger = "-PT" . $data->reminder . "M"; - $valarm->AddProperty("TRIGGER", $trigger); - $vevent->AddComponent($valarm); - } - if (isset($data->rtf)) { - $rtfparser = new rtf(); - $rtfparser->loadrtf(base64_decode($data->rtf)); - $rtfparser->output("ascii"); - $rtfparser->parse(); - $vevent->AddProperty("DESCRIPTION", $rtfparser->out); - } - if (isset($data->meetingstatus)) { - switch ($data->meetingstatus) { - case "1": - $vevent->AddProperty("STATUS", "TENTATIVE"); - $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "TENTATIVE"); - $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE"); - break; - case "3": - $vevent->AddProperty("STATUS", "CONFIRMED"); - $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CONFIRMED"); - $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "FALSE"); - break; - case "5": - case "7": - $vevent->AddProperty("STATUS", "CANCELLED"); - $vevent->AddProperty("X-MICROSOFT-CDO-BUSYSTATUS", "CANCELLED"); - $vevent->AddProperty("X-MICROSOFT-DISALLOW-COUNTER", "TRUE"); - break; - } - } - if (isset($data->attendees) && is_array($data->attendees)) { - //If there are attendees, we need to set ORGANIZER - //Some phones doesn't send the organizeremail, so we gotto get it somewhere else. - //Lets use the login here ($username) - if (!isset($data->organizeremail)) { - $vevent->AddProperty("ORGANIZER", sprintf("MAILTO:%s", $this->originalUsername)); - } - foreach ($data->attendees as $att) { - if (isset($att->name)) { - $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email), array("CN" => $att->name)); - } - else { - $vevent->AddProperty("ATTENDEE", sprintf("MAILTO:%s", $att->email)); - } - } - } - if (isset($data->body)) { - $vevent->AddProperty("DESCRIPTION", $data->body); - } - if (isset($data->asbody->data)) { - $vevent->AddProperty("DESCRIPTION", $data->asbody->data); - } - if (isset($data->categories) && is_array($data->categories)) { - $vevent->AddProperty("CATEGORIES", implode(",", $data->categories)); - } - -// X-MICROSOFT-CDO-APPT-SEQUENCE:0 -// X-MICROSOFT-CDO-OWNERAPPTID:2113393086 -// X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY -// X-MICROSOFT-CDO-IMPORTANCE:1 -// X-MICROSOFT-CDO-INSTTYPE:0 - - - return $vevent; - } - - /** - * Generate Recurrence - * @param string $rec - */ - private function _GenerateRecurrence($rec) { - $rrule = array(); - if (isset($rec->type)) { - $freq = ""; - switch ($rec->type) { - case "0": - $freq = "DAILY"; - break; - case "1": - $freq = "WEEKLY"; - break; - case "2": - case "3": - $freq = "MONTHLY"; - break; - case "5": - $freq = "YEARLY"; - break; - } - $rrule[] = "FREQ=" . $freq; - } - if (isset($rec->until)) { - $rrule[] = "UNTIL=" . gmdate("Ymd\THis\Z", $rec->until); - } - if (isset($rec->occurrences)) { - $rrule[] = "COUNT=" . $rec->occurrences; - } - if (isset($rec->interval)) { - $rrule[] = "INTERVAL=" . $rec->interval; - } - if (isset($rec->dayofweek)) { - $week = ''; - if (isset($rec->weekofmonth)) { - $week = $rec->weekofmonth; - } - $days = array(); - if (($rec->dayofweek & 1) == 1) { - if (empty($week)) { - $days[] = "SU"; - } - else { - $days[] = $week . "SU"; - } - } - if (($rec->dayofweek & 2) == 2) { - if (empty($week)) { - $days[] = "MO"; - } - else { - $days[] = $week . "MO"; - } - } - if (($rec->dayofweek & 4) == 4) { - if (empty($week)) { - $days[] = "TU"; - } - else { - $days[] = $week . "TU"; - } - } - if (($rec->dayofweek & 8) == 8) { - if (empty($week)) { - $days[] = "WE"; - } - else { - $days[] = $week . "WE"; - } - } - if (($rec->dayofweek & 16) == 16) { - if (empty($week)) { - $days[] = "TH"; - } - else { - $days[] = $week . "TH"; - } - } - if (($rec->dayofweek & 32) == 32) { - if (empty($week)) { - $days[] = "FR"; - } - else { - $days[] = $week . "FR"; - } - } - if (($rec->dayofweek & 64) == 64) { - if (empty($week)) { - $days[] = "SA"; - } - else { - $days[] = $week . "SA"; - } - } - $rrule[] = "BYDAY=" . implode(",", $days); - } - if (isset($rec->dayofmonth)) { - $rrule[] = "BYMONTHDAY=" . $rec->dayofmonth; - } - if (isset($rec->monthofyear)) { - $rrule[] = "BYMONTH=" . $rec->monthofyear; - } - return implode(";", $rrule); - } - - /** - * Convert a iCAL VTodo to ActiveSync format - * @param string $data - * @param ContentParameters $contentparameters - */ - private function _ParseVTodoToAS($data, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_ParseVTodoToAS(): Parsing VTodo")); - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - - $message = new SyncTask(); - $ical = new iCalComponent($data); - - $vtodos = $ical->GetComponents("VTODO"); - //Should only loop once - foreach ($vtodos as $vtodo) { - $message = $this->_ParseVTodoToSyncObject($vtodo, $message, $truncsize); - } - return $message; - } - - /** - * Parse 1 VEvent - * @param ical_vtodo $vtodo - * @param SyncAppointment(Exception) $message - * @param int $truncsize - */ - private function _ParseVTodoToSyncObject($vtodo, $message, $truncsize) { - //Default - $message->reminderset = "0"; - $message->importance = "1"; - $message->complete = "0"; - - $properties = $vtodo->GetProperties(); - foreach ($properties as $property) { - switch ($property->Name()) { - case "SUMMARY": - $message->subject = $property->Value(); - break; - - case "STATUS": - switch ($property->Value()) { - case "NEEDS-ACTION": - case "IN-PROCESS": - $message->complete = "0"; - break; - case "COMPLETED": - case "CANCELLED": - $message->complete = "1"; - break; - } - break; - - case "COMPLETED": - $message->datecompleted = Utils::MakeUTCDate($property->Value()); - break; - - case "DUE": - $message->utcduedate = Utils::MakeUTCDate($property->Value()); - break; - - case "PRIORITY": - $priority = $property->Value(); - if ($priority <= 3) - $message->importance = "0"; - if ($priority <= 6) - $message->importance = "1"; - if ($priority > 6) - $message->importance = "2"; - break; - - case "RRULE": - $message->recurrence = $this->_ParseRecurrence($property->Value(), "vtodo"); - break; - - case "CLASS": - switch ($property->Value()) { - case "PUBLIC": - $message->sensitivity = "0"; - break; - case "PRIVATE": - $message->sensitivity = "2"; - break; - case "CONFIDENTIAL": - $message->sensitivity = "3"; - break; - } - break; - - case "DTSTART": - $message->utcstartdate = Utils::MakeUTCDate($property->Value()); - break; - - case "SUMMARY": - $message->subject = $property->Value(); - break; - - case "CATEGORIES": - $categories = explode(",", $property->Value()); - $message->categories = $categories; - break; - } - } - - if (isset($message->recurrence)) { - $message->recurrence->start = $message->utcstartdate; - } - - $valarm = current($vtodo->GetComponents("VALARM")); - if ($valarm) { - $properties = $valarm->GetProperties(); - foreach ($properties as $property) { - if ($property->Name() == "TRIGGER") { - $parameters = $property->Parameters(); - if (array_key_exists("VALUE", $parameters) && $parameters["VALUE"] == "DATE-TIME") { - $message->remindertime = Utils::MakeUTCDate($property->Value()); - $message->reminderset = "1"; - } - elseif (!array_key_exists("VALUE", $parameters) || $parameters["VALUE"] == "DURATION") { - $val = str_replace("-", "", $property->Value()); - $interval = new DateInterval($val); - $start = date_create("@" . $message->utcstartdate); - $message->remindertime = date_timestamp_get(date_sub($start, $interval)); - $message->reminderset = "1"; - } - } - } - } - return $message; - } - - /** - * Generate a VTODO from a SyncAppointment(Exception) - * @param string $data - * @param string $id - * @return iCalComponent - */ - private function _ParseASTaskToVTodo($data, $id) { - $vtodo = new iCalComponent(); - $vtodo->SetType("VTODO"); - - if (isset($data->body)) { - $vtodo->AddProperty("DESCRIPTION", $data->body); - } - if (isset($data->asbody->data)) { - if (isset($data->nativebodytype) && $data->nativebodytype == SYNC_BODYPREFERENCE_RTF) { - $rtfparser = new rtf(); - $rtfparser->loadrtf(base64_decode($data->asbody->data)); - $rtfparser->output("ascii"); - $rtfparser->parse(); - $vtodo->AddProperty("DESCRIPTION", $rtfparser->out); - } - else { - $vtodo->AddProperty("DESCRIPTION", $data->asbody->data); - } - } - if (isset($data->complete)) { - if ($data->complete == "0") { - $vtodo->AddProperty("STATUS", "NEEDS-ACTION"); - } - else { - $vtodo->AddProperty("STATUS", "COMPLETED"); - } - } - if (isset($data->datecompleted)) { - $vtodo->AddProperty("COMPLETED", gmdate("Ymd\THis\Z", $data->datecompleted)); - } - if ($data->utcduedate) { - $vtodo->AddProperty("DUE", gmdate("Ymd\THis\Z", $data->utcduedate)); - } - if (isset($data->importance)) { - if ($data->importance == "1") { - $vtodo->AddProperty("PRIORITY", 6); - } - elseif ($data->importance == "2") { - $vtodo->AddProperty("PRIORITY", 9); - } - else { - $vtodo->AddProperty("PRIORITY", 1); - } - } - if (isset($data->recurrence)) { - $vtodo->AddProperty("RRULE", $this->_GenerateRecurrence($data->recurrence)); - } - if ($data->reminderset && $data->remindertime) { - $valarm = new iCalComponent(); - $valarm->SetType("VALARM"); - $valarm->AddProperty("ACTION", "DISPLAY"); - $valarm->AddProperty("TRIGGER;VALUE=DATE-TIME", gmdate("Ymd\THis\Z", $data->remindertime)); - $vtodo->AddComponent($valarm); - } - if (isset($data->sensitivity)) { - switch ($data->sensitivity) { - case "0": - $vtodo->AddProperty("CLASS", "PUBLIC"); - break; - - case "2": - $vtodo->AddProperty("CLASS", "PRIVATE"); - break; - - case "3": - $vtodo->AddProperty("CLASS", "CONFIDENTIAL"); - break; - } - } - if (isset($data->utcstartdate)) { - $vtodo->AddProperty("DTSTART", gmdate("Ymd\THis\Z", $data->utcstartdate)); - } - if (isset($data->subject)) { - $vtodo->AddProperty("SUMMARY", $data->subject); - } - if (isset($data->rtf)) { - $rtfparser = new rtf(); - $rtfparser->loadrtf(base64_decode($data->rtf)); - $rtfparser->output("ascii"); - $rtfparser->parse(); - $vtodo->AddProperty("DESCRIPTION", $rtfparser->out); - } - if (isset($data->categories) && is_array($data->categories)) { - $vtodo->AddProperty("CATEGORIES", implode(",", $data->categories)); - } - - return $vtodo; - } - - private function _GetDateFromUTC($format, $date, $tz_str) { - $timezone = $this->_GetTimezoneFromString($tz_str); - $dt = date_create('@' . $date); - date_timezone_set($dt, timezone_open($timezone)); - return date_format($dt, $format); - } - - //This returns a timezone that matches the timezonestring. - //We can't be sure this is the one you chose, as multiple timezones have same timezonestring - private function _GetTimezoneFromString($tz_string) { - //Get a list of all timezones - $identifiers = DateTimeZone::listIdentifiers(); - //Try the default timezone first - array_unshift($identifiers, date_default_timezone_get()); - foreach ($identifiers as $tz) { - $str = $this->_GetTimezoneString($tz, false); - if ($str == $tz_string) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->_GetTimezoneFromString(): Found timezone: '%s'.", $tz)); - return $tz; - } - } - return date_default_timezone_get(); - } - - /** - * Generate ActiveSync Timezone Packed String. - * @param string $timezone - * @param string $with_names - * @throws Exception - */ - private function _GetTimezoneString($timezone, $with_names = true) { - // UTC needs special handling - if ($timezone == "UTC") - return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0)); - try { - //Generate a timezone string (PHP 5.3 needed for this) - $timezone = new DateTimeZone($timezone); - $trans = $timezone->getTransitions(time()); - $stdTime = null; - $dstTime = null; - if (count($trans) < 3) { - throw new Exception(); - } - if ($trans[1]['isdst'] == 1) { - $dstTime = $trans[1]; - $stdTime = $trans[2]; - } - else { - $dstTime = $trans[2]; - $stdTime = $trans[1]; - } - $stdTimeO = new DateTime($stdTime['time']); - $stdFirst = new DateTime(sprintf("first sun of %s %s", $stdTimeO->format('F'), $stdTimeO->format('Y')), timezone_open("UTC")); - $stdBias = $stdTime['offset'] / -60; - $stdName = $stdTime['abbr']; - $stdYear = 0; - $stdMonth = $stdTimeO->format('n'); - $stdWeek = floor(($stdTimeO->format("j")-$stdFirst->format("j"))/7)+1; - $stdDay = $stdTimeO->format('w'); - $stdHour = $stdTimeO->format('H'); - $stdMinute = $stdTimeO->format('i'); - $stdTimeO->add(new DateInterval('P7D')); - if ($stdTimeO->format('n') != $stdMonth) { - $stdWeek = 5; - } - $dstTimeO = new DateTime($dstTime['time']); - $dstFirst = new DateTime(sprintf("first sun of %s %s", $dstTimeO->format('F'), $dstTimeO->format('Y')), timezone_open("UTC")); - $dstName = $dstTime['abbr']; - $dstYear = 0; - $dstMonth = $dstTimeO->format('n'); - $dstWeek = floor(($dstTimeO->format("j")-$dstFirst->format("j"))/7)+1; - $dstDay = $dstTimeO->format('w'); - $dstHour = $dstTimeO->format('H'); - $dstMinute = $dstTimeO->format('i'); - $dstTimeO->add(new DateInterval('P7D')); - if ($dstTimeO->format('n') != $dstMonth) { - $dstWeek = 5; - } - $dstBias = ($dstTime['offset'] - $stdTime['offset']) / -60; - if ($with_names) { - return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', $stdBias, $stdName, 0, $stdMonth, $stdDay, $stdWeek, $stdHour, $stdMinute, 0, 0, 0, $dstName, 0, $dstMonth, $dstDay, $dstWeek, $dstHour, $dstMinute, 0, 0, $dstBias)); - } - else { - return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', $stdBias, '', 0, $stdMonth, $stdDay, $stdWeek, $stdHour, $stdMinute, 0, 0, 0, '', 0, $dstMonth, $dstDay, $dstWeek, $dstHour, $dstMinute, 0, 0, $dstBias)); - } - } - catch (Exception $e) { - // If invalid timezone is given, we return UTC - return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0)); - } - return base64_encode(pack('la64vvvvvvvvla64vvvvvvvvl', 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, 0)); - } -} \ No newline at end of file diff --git a/sources/backend/caldav/config.php b/sources/backend/caldav/config.php deleted file mode 100644 index 7c5c9fa..0000000 --- a/sources/backend/caldav/config.php +++ /dev/null @@ -1,74 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ************************ -// BackendCalDAV settings -// ************************ - -// Server protocol: http or https -define('CALDAV_PROTOCOL', 'https'); - -// Server name -define('CALDAV_SERVER', 'caldavserver.domain.com'); - -// Server port -define('CALDAV_PORT', '443'); - -// Path -define('CALDAV_PATH', '/caldav.php/%u/'); - -// Default CalDAV folder (calendar folder/principal). This will be marked as the default calendar in the mobile -define('CALDAV_PERSONAL', 'PRINCIPAL'); - -// If the CalDAV server supports the sync-collection operation -// DAViCal, SOGo and SabreDav support it -// SabreDav version must be at least 1.9.0, otherwise set this to false -// Setting this to false will work with most servers, but it will be slower -define('CALDAV_SUPPORTS_SYNC', false); - - -// Maximum period to sync. -// Some servers don't support more than 10 years so you will need to change this -define('CALDAV_MAX_SYNC_PERIOD', 2147483647); \ No newline at end of file diff --git a/sources/backend/carddav/README b/sources/backend/carddav/README deleted file mode 100644 index e0228d4..0000000 --- a/sources/backend/carddav/README +++ /dev/null @@ -1,12 +0,0 @@ -This is a CardDAV backend based in the vcarddir backend. - -It supports DAViCal, Sogo, OwnCloud, SabreDav... and should works with any carddav server. So if it doesn't work with your server, please open a issue. - -It supports ChangesSink method that will detect and send faster changes to your device. - -DAViCal implements the SYNC operation, it's a very fast method to detect changes in your vcards. -The others servers don't implement it, so the code will fallback to a slower method (suggest your carddav server developers to implement it!!). - -This is controlled with a flag in the config.php file. - -Also, it can autodetect multiple addressbooks and will present them to the mobile device as an unique addressbook (only iOS supports multiple addressbook). diff --git a/sources/backend/carddav/REQUIREMENTS b/sources/backend/carddav/REQUIREMENTS deleted file mode 100644 index ada7371..0000000 --- a/sources/backend/carddav/REQUIREMENTS +++ /dev/null @@ -1,5 +0,0 @@ -REQUIREMENTS: -php-curl -php-xsl - -CardDAV server (DAViCal, Sabredav, Sogo, Owncloud...) \ No newline at end of file diff --git a/sources/backend/carddav/THANKS b/sources/backend/carddav/THANKS deleted file mode 100644 index f213642..0000000 --- a/sources/backend/carddav/THANKS +++ /dev/null @@ -1,8 +0,0 @@ -*Drenalina SRL (www.drenalina.com)* sponsored the development of the following features in the BackendCardDAV, any existing bug it's my fault not theirs ;-) -Thank you very much for helping to improve it!! - - - - Autodetecting addressbooks within a DAV principal. - - Merging multiple addressbooks so the device will see a unique one. Only iOS based devices support multiple addressbooks, so we will merge them for now. - - Selecting default addressbook to store new contacts created from the device. - - GAL addressbook and GAL search. \ No newline at end of file diff --git a/sources/backend/carddav/carddav.php b/sources/backend/carddav/carddav.php deleted file mode 100644 index 21ad6ba..0000000 --- a/sources/backend/carddav/carddav.php +++ /dev/null @@ -1,1462 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/carddav/config.php"); - -class BackendCardDAV extends BackendDiff implements ISearchProvider { - - private $domain = ''; - private $username = ''; - private $url = null; - /** - * @var carddav_backend - */ - private $server = null; - private $default_url = null; - private $gal_url = null; - - // Android only supports synchronizing 1 AddressBook per account, this is the foldername for Z-Push - private $foldername = "contacts"; - - // We can have multiple addressbooks, but the mobile device will only see one (all of them merged) - private $addressbooks; - - private $changessinkinit; - private $contactsetag; - private $sinkdata; - - /** - * Constructor - * - */ - public function BackendCardDAV() { - if (!function_exists("curl_init")) { - throw new FatalException("BackendCardDAV(): php-curl is not found", 0, null, LOGLEVEL_FATAL); - } - - $this->addressbooks = array(); - $this->changessinkinit = false; - $this->contactsetag = array(); - $this->sinkdata = array(); - } - - /** - * Authenticates the user - NOT EFFECTIVELY IMPLEMENTED - * Normally some kind of password check would be done here. - * Alternatively, the password could be ignored and an Apache - * authentication via mod_auth_* could be done - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - */ - public function Logon($username, $domain, $password) { - $this->url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_PATH)); - $this->default_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_DEFAULT_PATH)); - if (defined('CARDDAV_GAL_PATH')) { - $this->gal_url = CARDDAV_PROTOCOL . '://' . CARDDAV_SERVER . ':' . CARDDAV_PORT . str_replace("%d", $domain, str_replace("%u", $username, CARDDAV_GAL_PATH)); - } - else { - $this->gal_url = false; - } - $this->server = new carddav_backend($this->url, CARDDAV_URL_VCARD_EXTENSION); - $this->server->set_auth($username, $password); - - if (($connected = $this->server->check_connection())) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->Logon(): User '%s' is authenticated on '%s'", $username, $this->url)); - $this->username = $username; - $this->domain = $domain; - - // Autodiscover all the addressbooks - $this->discoverAddressbooks(); - } - else { - //TODO: get error message - $error = ''; - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->Logon(): User '%s' failed to authenticate on '%s': %s", $username, $this->url, $error)); - $this->server = null; - } - - return $connected; - } - - /** - * Logs off - * - * @access public - * @return boolean - */ - public function Logoff() { - if ($this->server != null) { - $this->server->disconnect(); - unset($this->server); - } - - $this->SaveStorages(); - - unset($this->contactsetag); - unset($this->sinkdata); - unset($this->addressbooks); - - ZLog::Write(LOGLEVEL_DEBUG, "BackendCardDAV->Logoff(): disconnected from CARDDAV server"); - - return true; - } - - /** - * Sends an e-mail - * Not implemented here - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - return false; - } - - /** - * Returns the waste basket - * Not implemented here - * - * @access public - * @return string - */ - public function GetWasteBasket() { - return false; - } - - /** - * Returns the content of the named attachment as stream - * Not implemented here - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - return false; - } - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * The CardDAV backend simulates a sink by polling revision dates from the vcards - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - return true; - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBackend->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if found can not be found - */ - public function ChangesSinkInitialize($folderid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSinkInitialize(): folderid '%s'", $folderid)); - - // We don't need the actual cards, we only need to get the changes since this moment - $init_ok = true; - foreach ($this->addressbooks as $addressbook) { - try { - $this->server->set_url($addressbook); - $this->sinkdata[$addressbook] = $this->server->do_sync(true, false, CARDDAV_SUPPORTS_SYNC); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSinkInitialize - Error doing the initial sync for '%s': %s", $addressbook, $ex->getMessage())); - $init_ok = false; - } - - if ($this->sinkdata[$addressbook] === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSinkInitialize - Error initializing the sink for '%s'", $addressbook)); - $init_ok = false; - } - - if (CARDDAV_SUPPORTS_SYNC) { - // we don't need to store the sinkdata if the carddav server supports native sync - unset($this->sinkdata[$addressbook]); - } - } - - $this->changessinkinit = $init_ok; - - return $this->changessinkinit; - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - $notifications = array(); - $stopat = time() + $timeout - 1; - $changed = false; - - //We can get here and the ChangesSink not be initialized yet - if (!$this->changessinkinit) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Not initialized ChangesSink, sleep and exit")); - // We sleep and do nothing else - sleep($timeout); - return $notifications; - } - - // only check once to reduce pressure in the DAV server - foreach ($this->addressbooks as $addressbook) { - $vcards = false; - try { - $this->server->set_url($addressbook); - $vcards = $this->server->do_sync(false, false, CARDDAV_SUPPORTS_SYNC); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSink - Error resyncing vcards: %s", $ex->getMessage())); - } - - if ($vcards === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangesSink - Error getting the changes")); - return false; - } - else { - $xml_vcards = new SimpleXMLElement($vcards); - - if (CARDDAV_SUPPORTS_SYNC) { - if (count($xml_vcards->element) > 0) { - $changed = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected")); - } - } - else { - $xml_sinkdata = new SimpleXMLElement($this->sinkdata[$addressbook]); - if (count($xml_vcards->element) != count($xml_sinkdata->element)) { - // If the number of cards is different, we know for sure, there are changes - $changed = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected")); - } - else { - // If it's the same we need to check vcard to vcard, or the original strings - if (strcmp($this->sinkdata[$addressbook], $vcards) != 0) { - $changed = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangesSink - Changes detected")); - } - } - unset($xml_sinkdata); - } - - unset($vcards); - unset($xml_vcards); - } - - if ($changed) { - $notifications[] = $this->foldername; - } - } - - // Wait to timeout - if (empty($notifications)) { - while ($stopat > time()) { - sleep(1); - } - } - - return $notifications; - } - - /**---------------------------------------------------------------------------------------------------------- - * implemented DiffBackend methods - */ - - /** - * Returns a list (array) of folders. - * In simple implementations like this one, probably just one folder is returned. - * - * @access public - * @return array - */ - public function GetFolderList() { - ZLog::Write(LOGLEVEL_DEBUG, 'BackendCardDAV::GetFolderList()'); - - // The mobile will only see one - $addressbooks = array(); - $addressbook = $this->StatFolder($this->foldername); - $addressbooks[] = $addressbook; - - return $addressbooks; - } - - /** - * Returns an actual SyncFolder object - * - * @param string $id id of the folder - * - * @access public - * @return object SyncFolder with information - */ - public function GetFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::GetFolder('%s')", $id)); - - $addressbook = false; - - if ($id == $this->foldername) { - $addressbook = new SyncFolder(); - $addressbook->serverid = $id; - $addressbook->parentid = "0"; - $addressbook->displayname = str_replace("%d", $this->domain, str_replace("%u", $this->username, CARDDAV_CONTACTS_FOLDER_NAME)); - $addressbook->type = SYNC_FOLDER_TYPE_CONTACT; - } - - return $addressbook; - } - - /** - * Returns folder stats. An associative array with properties is expected. - * - * @param string $id id of the folder - * - * @access public - * @return array - */ - public function StatFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::StatFolder('%s')", $id)); - - $addressbook = $this->GetFolder($id); - - $stat = array(); - $stat["id"] = $id; - $stat["parent"] = $addressbook->parentid; - $stat["mod"] = $addressbook->displayname; - - return $stat; - } - - /** - * Creates or modifies a folder - * Not implemented here - * - * @param string $folderid id of the parent folder - * @param string $oldid if empty -> new folder created, else folder is to be renamed - * @param string $displayname new folder name (to be created, or to be renamed to) - * @param int $type folder type - * - * @access public - * @return boolean status - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function ChangeFolder($folderid, $oldid, $displayname, $type) { - return false; - } - - /** - * Deletes a folder - * Not implemented here - * - * @param string $id - * @param string $parent is normally false - * - * @access public - * @return boolean status - false if e.g. does not exist - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function DeleteFolder($id, $parentid) { - return false; - } - - /** - * Returns a list (array) of messages - * - * @param string $folderid id of the parent folder - * @param long $cutoffdate timestamp in the past from which on messages should be returned - * - * @access public - * @return array/false array with messages or false if folder is not available - */ - public function GetMessageList($folderid, $cutoffdate) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessageList('%s', '%s')", $folderid, $cutoffdate)); - - $messages = array(); - - foreach ($this->addressbooks as $addressbook) { - $addressbookId = $this->convertAddressbookUrl($addressbook); - - $vcards = false; - try { - // We don't need the actual vcards here, we only need a list of all them - // This petition is always "initial", and we don't "include_vcards" - $this->server->set_url($addressbook); - $vcards = $this->server->do_sync(true, false, CARDDAV_SUPPORTS_SYNC); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessageList - Error getting the vcards in '%s': %s", $addressbook, $ex->getMessage())); - } - - if ($vcards === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessageList - Error getting the vcards")); - } - else { - $xml_vcards = new SimpleXMLElement($vcards); - foreach ($xml_vcards->element as $vcard) { - $id = $addressbookId . "-" . $vcard->id->__toString(); - $this->contactsetag[$id] = $vcard->etag->__toString(); - $messages[] = $this->StatMessage($folderid, $id); - } - } - } - - return $messages; - } - - /** - * Returns the actual SyncXXX object type. - * - * @param string $folderid id of the parent folder - * @param string $id id of the message - * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) - * - * @access public - * @return object/false false if the message could not be retrieved - */ - public function GetMessage($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetMessage('%s', '%s')", $folderid, $id)); - - $message = false; - $addressbookId = $this->getAddressbookIdFromVcard($id); - $vcardId = $this->getVcardId($id); - $addressbookUrl = $this->getAddressbookFromId($addressbookId); - - if ($addressbookUrl !== false) { - $xml_vcard = false; - try { - $this->server->set_url($addressbookUrl); - $xml_vcard = $this->server->get_xml_vcard($vcardId); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage - Error getting vcard '%s' in '%s': %s", $vcardId, $addressbookId, $ex->getMessage())); - } - - if ($xml_vcard !== false) { - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - $xml_data = new SimpleXMLElement($xml_vcard); - $message = $this->ParseFromVCard($xml_data->element[0]->vcard->__toString(), $truncsize); - } - } - - if ($message === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetMessage(): vCard not found")); - } - - return $message; - } - - - /** - * Returns message stats, analogous to the folder stats from StatFolder(). - * - * @param string $folderid id of the folder - * @param string $id id of the message - * - * @access public - * @return array - */ - public function StatMessage($folderid, $id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->StatMessage('%s', '%s')", $folderid, $id)); - - $message = array(); - if (!isset($this->contactsetag[$id])) { - $addressbookId = $this->getAddressbookIdFromVcard($id); - $vcardId = $this->getVcardId($id); - $addressbookUrl = $this->getAddressbookFromId($addressbookId); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->StatMessage - No contactsetag found, getting vcard '%s' in '%s'", $vcardId, $addressbookId)); - if ($addressbookUrl !== false) { - $xml_vcard = false; - try { - $this->server->set_url($addressbookUrl); - $xml_vcard = $this->server->get_xml_vcard($vcardId); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->StatMessage - Error getting vcard '%s' in '%s': %s", $vcardId, $addressbookId, $ex->getMessage())); - } - - if ($xml_vcard !== false) { - $vcard = new SimpleXMLElement($xml_vcard); - $this->contactsetag[$id] = $vcard->element[0]->etag->__toString(); - unset($vcard); - } - unset($xml_vcard); - } - } - $message["mod"] = $this->contactsetag[$id]; - $message["id"] = $id; - $message["flags"] = 1; - - return $message; - } - - /** - * Called when a message has been changed on the mobile. - * This functionality is not available for emails. - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param SyncXXX $message the SyncObject containing a message - * @param ContentParameters $contentParameters - * - * @access public - * @return array same return value as StatMessage() - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function ChangeMessage($folderid, $id, $message, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangeMessage('%s', '%s')", $folderid, $id)); - - $vcard_text = $this->ParseToVCard($message); - - if ($vcard_text === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error converting message to vCard")); - } - else { - ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendCardDAV->ChangeMessage - vCard\n%s\n", $vcard_text)); - - $updated = false; - if (strlen($id) == 0) { - //no id, new vcard - try { - $addressbookId = $this->getAddressbookFromUrl($this->default_url); - if ($addressbookId === false) { - $addressbookId = $this->getAddressbookFromUrl($this->addressbooks[0]); - $this->server->set_url($this->addressbooks[0]); - } - else { - $this->server->set_url($this->default_url); - } - - $updated = $this->server->add($vcard_text); - if ($updated !== false) { - $id = $addressbookId . "-" . $updated; - } - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error adding vcard '%s' : %s", $id, $ex->getMessage())); - } - } - else { - //id, update vcard - - $vcardId = $this->getVcardId($id); - $addressbookUrl = $this->getAddressbookFromId($this->getAddressbookIdFromVcard($id)); - - if ($addressbookUrl !== false) { - try { - $this->server->set_url($addressbookUrl); - $updated = $this->server->update($vcard_text, $vcardId); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - Error updating vcard '%s' : %s", $id, $ex->getMessage())); - } - } - } - - if ($updated !== false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->ChangeMessage - vCard updated")); - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->ChangeMessage - vCard not updated")); - } - } - - return $this->StatMessage($folderid, $id); - } - - /** - * Changes the 'read' flag of a message on disk - * Not implemented here - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param int $flags read flag of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function SetReadFlag($folderid, $id, $flags, $contentParameters) { - return false; - } - - /** - * Called when the user has requested to delete (really delete) a message - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function DeleteMessage($folderid, $id, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->DeleteMessage('%s', '%s')", $folderid, $id)); - - $deleted = false; - - $vcardId = $this->getVcardId($id); - $addressbookUrl = $this->getAddressbookFromId($this->getAddressbookIdFromVcard($id)); - - if ($addressbookUrl !== false) { - try { - $this->server->set_url($addressbookUrl); - $deleted = $this->server->delete($vcardId); - } - catch (Exception $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->DeleteMessage - Error deleting vcard: %s", $ex->getMessage())); - } - } - - if ($deleted) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->DeleteMessage - vCard deleted")); - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->DeleteMessage - cannot delete vCard")); - } - - return $deleted; - } - - /** - * Called when the user moves an item on the PDA from one folder to another - * Not implemented here - * - * @param string $folderid id of the source folder - * @param string $id id of the message - * @param string $newfolderid id of the destination folder - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions - */ - public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) { - return false; - } - - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - // TODO: - return false; - } - - - /** - * Indicates which AS version is supported by the backend. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_14; - } - - - /** - * Returns the BackendCardDAV as it implements the ISearchProvider interface - * This could be overwritten by the global configuration - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider() { - return $this; - } - - - /**---------------------------------------------------------------------------------------------------------- - * public ISearchProvider methods - */ - - /** - * Indicates if a search type is supported by this SearchProvider - * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - if ($this->gal_url !== false) { - return ($searchtype == ISearchProvider::SEARCH_GAL); - } - else { - return false; - } - } - - - /** - * Queries the CardDAV backend - * - * @param string $searchquery string to be searched for - * @param string $searchrange specified searchrange - * - * @access public - * @return array search results - */ - public function GetGALSearchResults($searchquery, $searchrange) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults(%s, %s)", $searchquery, $searchrange)); - if ($this->gal_url !== false && $this->server !== false) { - // Don't search if the length is < 5, we are typing yet - if (strlen($searchquery) < CARDDAV_GAL_MIN_LENGTH) { - return false; - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults searching: %s", $this->url)); - try { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults server is null? %d", $this->server == null)); - $this->server->set_url($this->gal_url); - $vcards = $this->server->search_vcards(str_replace("<", "", str_replace(">", "", $searchquery)), 15, true, false, - defined('CARDDAV_SUPPORTS_FN_SEARCH') ? CARDDAV_SUPPORTS_FN_SEARCH : false); - } - catch (Exception $e) { - $vcards = false; - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetGALSearchResults : Error in search %s", $e->getMessage())); - } - if ($vcards === false) { - ZLog::Write(LOGLEVEL_ERROR, "BackendCardDAV->GetGALSearchResults : Error in search query. Search aborted"); - return false; - } - - $xml_vcards = new SimpleXMLElement($vcards); - unset($vcards); - - // range for the search results, default symbian range end is 50, wm 99, - // so we'll use that of nokia - $rangestart = 0; - $rangeend = 50; - - if ($searchrange != '0') { - $pos = strpos($searchrange, '-'); - $rangestart = substr($searchrange, 0, $pos); - $rangeend = substr($searchrange, ($pos + 1)); - } - $items = array(); - - // TODO the limiting of the searchresults could be refactored into Utils as it's probably used more than once - $querycnt = $xml_vcards->count(); - //do not return more results as requested in range - $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt == 0 ? 1 : $querycnt; - $items['range'] = $rangestart.'-'.($querylimit - 1); - $items['searchtotal'] = $querycnt; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->GetGALSearchResults : %s entries found, returning %s to %s", $querycnt, $rangestart, $querylimit)); - - $i = 0; - $rc = 0; - foreach ($xml_vcards->element as $xml_vcard) { - if ($i >= $rangestart && $i < $querylimit) { - $contact = $this->ParseFromVCard($xml_vcard->vcard->__toString()); - if ($contact === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCardDAV->GetGALSearchResults : error converting vCard to AS contact\n%s\n", $xml_vcard->vcard->__toString())); - } - else { - $items[$rc][SYNC_GAL_EMAILADDRESS] = $contact->email1address; - if (isset($contact->fileas)) { - $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->fileas; - } - else if (isset($contact->firstname) || isset($contact->middlename) || isset($contact->lastname)) { - $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->firstname . (isset($contact->middlename) ? " " . $contact->middlename : "") . (isset($contact->lastname) ? " " . $contact->lastname : ""); - } - else { - $items[$rc][SYNC_GAL_DISPLAYNAME] = $contact->email1address; - } - if (isset($contact->firstname)) { - $items[$rc][SYNC_GAL_FIRSTNAME] = $contact->firstname; - } - else { - $items[$rc][SYNC_GAL_FIRSTNAME] = ""; - } - if (isset($contact->lastname)) { - $items[$rc][SYNC_GAL_LASTNAME] = $contact->lastname; - } - else { - $items[$rc][SYNC_GAL_LASTNAME] = ""; - } - if (isset($contact->business2phonenumber)) { - $items[$rc][SYNC_GAL_PHONE] = $contact->business2phonenumber; - } - if (isset($contact->home2phonenumber)) { - $items[$rc][SYNC_GAL_HOMEPHONE] = $contact->home2phonenumber; - } - if (isset($contact->mobilephonenumber)) { - $items[$rc][SYNC_GAL_MOBILEPHONE] = $contact->mobilephonenumber; - } - if (isset($contact->title)) { - $items[$rc][SYNC_GAL_TITLE] = $contact->title; - } - if (isset($contact->companyname)) { - $items[$rc][SYNC_GAL_COMPANY] = $contact->companyname; - } - if (isset($contact->department)) { - $items[$rc][SYNC_GAL_OFFICE] = $contact->department; - } - if (isset($contact->nickname)) { - $items[$rc][SYNC_GAL_ALIAS] = $contact->nickname; - } - unset($contact); - $rc++; - } - } - $i++; - } - - unset($xml_vcards); - return $items; - } - else { - unset($xml_vcards); - return false; - } - } - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo) { - return false; - } - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - return true; - } - - /** - * Disconnects from CardDAV - * - * @access public - * @return boolean - */ - public function Disconnect() { - return true; - } - - - /**---------------------------------------------------------------------------------------------------------- - * private vcard-specific internals - */ - - - /** - * Escapes a string - * - * @param string $data string to be escaped - * - * @access private - * @return string - */ - private function escape($data) { - if (is_array($data)) { - foreach ($data as $key => $val) { - $data[$key] = $this->escape($val); - } - return $data; - } - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", "\n", $data); - $data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data); - return $data; - } - - /** - * Un-escapes a string - * - * @param string $data string to be un-escaped - * - * @access private - * @return string - */ - private function unescape($data) { - $data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data); - return $data; - } - - /** - * Converts the vCard into SyncContact. - * See RFC 6350 for vCard format details. - * - * @param string $data string with the vcard - * @param int $truncsize truncate size requested - * @return SyncContact - */ - private function ParseFromVCard($data, $truncsize = -1) { - ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendCardDAV->ParseFromVCard : vCard\n%s\n", $data)); - - $types = array ('dom' => 'type', 'intl' => 'type', 'postal' => 'type', 'parcel' => 'type', 'home' => 'type', 'work' => 'type', - 'pref' => 'type', 'voice' => 'type', 'fax' => 'type', 'msg' => 'type', 'cell' => 'type', 'pager' => 'type', - 'bbs' => 'type', 'modem' => 'type', 'car' => 'type', 'isdn' => 'type', 'video' => 'type', - 'aol' => 'type', 'applelink' => 'type', 'attmail' => 'type', 'cis' => 'type', 'eworld' => 'type', - 'internet' => 'type', 'ibmmail' => 'type', 'mcimail' => 'type', - 'powershare' => 'type', 'prodigy' => 'type', 'tlx' => 'type', 'x400' => 'type', - 'gif' => 'type', 'cgm' => 'type', 'wmf' => 'type', 'bmp' => 'type', 'met' => 'type', 'pmb' => 'type', 'dib' => 'type', - 'pict' => 'type', 'tiff' => 'type', 'pdf' => 'type', 'ps' => 'type', 'jpeg' => 'type', 'qtime' => 'type', - 'mpeg' => 'type', 'mpeg2' => 'type', 'avi' => 'type', - 'wave' => 'type', 'aiff' => 'type', 'pcm' => 'type', - 'x509' => 'type', 'pgp' => 'type', 'text' => 'value', 'inline' => 'value', 'url' => 'value', 'cid' => 'value', 'content-id' => 'value', - '7bit' => 'encoding', '8bit' => 'encoding', 'quoted-printable' => 'encoding', 'base64' => 'encoding', - ); - - // Parse the vcard - $message = new SyncContact(); - - $data = str_replace("\x00", '', $data); - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", "\n", $data); - $data = preg_replace('/(\n)([ \t])/i', '', $data); - - $lines = explode("\n", $data); - - $vcard = array(); - foreach ($lines as $line) { - if (trim($line) == '') - continue; - $pos = strpos($line, ':'); - if ($pos === false) - continue; - - $field = trim(substr($line, 0, $pos)); - $value = trim(substr($line, $pos + 1)); - - $fieldparts = preg_split('/(? $v) { - $val[$i] = quoted_printable_decode($v); - } - break; - case 'b': - case 'base64': - foreach ($val as $i => $v) { - $val[$i] = base64_decode($v); - } - break; - } - } else { - foreach ($val as $i => $v) { - $val[$i] = $this->unescape($v); - } - } - $fieldvalue['val'] = $val; - $vcard[$type][] = $fieldvalue; - } - - if (isset($vcard['email'][0]['val'][0])) - $message->email1address = $vcard['email'][0]['val'][0]; - if (isset($vcard['email'][1]['val'][0])) - $message->email2address = $vcard['email'][1]['val'][0]; - if (isset($vcard['email'][2]['val'][0])) - $message->email3address = $vcard['email'][2]['val'][0]; - - if (isset($vcard['tel'])) { - foreach ($vcard['tel'] as $tel) { - if (!isset($tel['type'])) { - $tel['type'] = array(); - } - if (in_array('car', $tel['type'])) { - $message->carphonenumber = $tel['val'][0]; - } - elseif (in_array('pager', $tel['type'])) { - $message->pagernumber = $tel['val'][0]; - } - elseif (in_array('cell', $tel['type'])) { - $message->mobilephonenumber = $tel['val'][0]; - } - elseif (in_array('home', $tel['type'])) { - if (in_array('fax', $tel['type'])) { - $message->homefaxnumber = $tel['val'][0]; - } - elseif (empty($message->homephonenumber)) { - $message->homephonenumber = $tel['val'][0]; - } - else { - $message->home2phonenumber = $tel['val'][0]; - } - } - elseif (in_array('work', $tel['type'])) { - if (in_array('fax', $tel['type'])) { - $message->businessfaxnumber = $tel['val'][0]; - } - elseif (empty($message->businessphonenumber)) { - $message->businessphonenumber = $tel['val'][0]; - } - else { - $message->business2phonenumber = $tel['val'][0]; - } - } - elseif (empty($message->homephonenumber)) { - $message->homephonenumber = $tel['val'][0]; - } - elseif (empty($message->home2phonenumber)) { - $message->home2phonenumber = $tel['val'][0]; - } - else { - $message->radiophonenumber = $tel['val'][0]; - } - } - } - - //;;street;city;state;postalcode;country - if (isset($vcard['adr'])) { - foreach ($vcard['adr'] as $adr) { - if (empty($adr['type'])) { - $a = 'other'; - } - elseif (in_array('home', $adr['type'])) { - $a = 'home'; - } - elseif (in_array('work', $adr['type'])) { - $a = 'business'; - } - else { - $a = 'other'; - } - if (!empty($adr['val'][2])) { - $b=$a.'street'; - $message->$b = $adr['val'][2]; - } - if (!empty($adr['val'][3])) { - $b=$a.'city'; - $message->$b = $adr['val'][3]; - } - if (!empty($adr['val'][4])) { - $b=$a.'state'; - $message->$b = $adr['val'][4]; - } - if (!empty($adr['val'][5])) { - $b=$a.'postalcode'; - $message->$b = $adr['val'][5]; - } - if (!empty($adr['val'][6])) { - $b=$a.'country'; - $message->$b = $adr['val'][6]; - } - } - } - - if (!empty($vcard['fn'][0]['val'][0])) - $message->fileas = $vcard['fn'][0]['val'][0]; - if (!empty($vcard['n'][0]['val'][0])) - $message->lastname = $vcard['n'][0]['val'][0]; - if (!empty($vcard['n'][0]['val'][1])) - $message->firstname = $vcard['n'][0]['val'][1]; - if (!empty($vcard['n'][0]['val'][2])) - $message->middlename = $vcard['n'][0]['val'][2]; - if (!empty($vcard['n'][0]['val'][3])) - $message->title = $vcard['n'][0]['val'][3]; - if (!empty($vcard['n'][0]['val'][4])) - $message->suffix = $vcard['n'][0]['val'][4]; - if (!empty($vcard['nickname'][0]['val'][0])) - $message->nickname = $vcard['nickname'][0]['val'][0]; - if (!empty($vcard['bday'][0]['val'][0])) { - $tz = date_default_timezone_get(); - date_default_timezone_set('UTC'); - $message->birthday = strtotime($vcard['bday'][0]['val'][0]); - date_default_timezone_set($tz); - } - if (!empty($vcard['org'][0]['val'][0])) - $message->companyname = $vcard['org'][0]['val'][0]; - if (!empty($vcard['note'][0]['val'][0])) { - if (Request::GetProtocolVersion() >= 12.0) { - $message->asbody = new SyncBaseBody(); - $message->asbody->type = SYNC_BODYPREFERENCE_PLAIN; - $message->asbody->data = $vcard['note'][0]['val'][0]; - if ($truncsize > 0 && $truncsize < strlen($message->asbody->data)) { - $message->asbody->truncated = 1; - $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $truncsize); - } - else { - $message->asbody->truncated = 0; - } - - $message->asbody->estimatedDataSize = strlen($message->asbody->data); - } - else { - $message->body = $vcard['note'][0]['val'][0]; - if ($truncsize > 0 && $truncsize < strlen($message->body)) { - $message->bodytruncated = 1; - $message->body = Utils::Utf8_truncate($message->body, $truncsize); - } - else { - $message->bodytruncated = 0; - } - $message->bodysize = strlen($message->body); - } - } - - // Support both ROLE and TITLE (RFC 6350 § 6.6.1 / § 6.6.2) as mapped to JobTitle - if (!empty($vcard['role'][0]['val'][0])) - $message->jobtitle = $vcard['role'][0]['val'][0]; - if (!empty($vcard['title'][0]['val'][0])) - $message->jobtitle = $vcard['title'][0]['val'][0]; - - if (!empty($vcard['url'][0]['val'][0])) - $message->webpage = $vcard['url'][0]['val'][0]; - if (!empty($vcard['categories'][0]['val'])) - $message->categories = $vcard['categories'][0]['val']; - - if (!empty($vcard['photo'][0]['val'][0])) - $message->picture = base64_encode($vcard['photo'][0]['val'][0]); - - return $message; - } - - /** - * Convert a SyncObject into vCard. - * - * @param SyncContact $message AS Contact - * @return string vcard text - */ - private function ParseToVCard($message) { - // http://tools.ietf.org/html/rfc6350 - $mapping = array( - 'fileas' => 'FN', - 'lastname;firstname;middlename;title;suffix' => 'N', - 'email1address' => 'EMAIL;PREF=1', - 'email2address' => 'EMAIL;PREF=2', - 'email3address' => 'EMAIL;PREF=3', - 'businessphonenumber' => 'TEL;TYPE=WORK,VOICE', - 'business2phonenumber' => 'TEL;TYPE=WORK,VOICE', - 'businessfaxnumber' => 'TEL;TYPE=WORK,FAX', - 'homephonenumber' => 'TEL;TYPE=HOME,VOICE', - 'home2phonenumber' => 'TEL;TYPE=HOME,VOICE', - 'homefaxnumber' => 'TEL;TYPE=HOME,FAX', - 'mobilephonenumber' => 'TEL;TYPE=CELL', - 'carphonenumber' => 'TEL;TYPE=VOICE', - 'pagernumber' => 'TEL;TYPE=PAGER', - ';;businessstreet;businesscity;businessstate;businesspostalcode;businesscountry' => 'ADR;TYPE=WORK', - ';;homestreet;homecity;homestate;homepostalcode;homecountry' => 'ADR;TYPE=HOME', - ';;otherstreet;othercity;otherstate;otherpostalcode;othercountry' => 'ADR', - 'companyname' => 'ORG', - 'body' => 'NOTE', - 'jobtitle' => 'ROLE', - 'webpage' => 'URL', - 'nickname' => 'NICKNAME' - ); - - $data = "BEGIN:VCARD\nVERSION:3.0\nPRODID:Z-Push\n"; - foreach ($mapping as $k => $v) { - $val = ''; - $ks = explode(';', $k); - foreach ($ks as $i) { - if (!empty($message->$i)) - $val .= $this->escape($message->$i); - $val.=';'; - } - if ($k == 'body' && isset($message->asbody)) { - $val = $message->asbody->data; - } - if (empty($val) || preg_match('/^(\;)+$/', $val) == 1) - continue; - // Remove trailing ; - $val = substr($val, 0, -1); - if (strlen($val) > 50) { - $data .= $v.":\n\t".substr(chunk_split($val, 50, "\n\t"), 0, -1); - } - else { - $data .= $v.':'.$val."\n"; - } - } - if (!empty($message->categories)) - $data .= 'CATEGORIES:'.implode(',', $message->categories)."\n"; - if (!empty($message->picture)) - $data .= 'PHOTO;ENCODING=BASE64;TYPE=JPEG:'."\n\t".substr(chunk_split($message->picture, 50, "\n\t"), 0, -1); - if (isset($message->birthday)) - $data .= 'BDAY:'.date('Y-m-d', $message->birthday)."\n"; - $data .= "END:VCARD"; - - // http://en.wikipedia.org/wiki/VCard - // TODO: add support for v4.0 - // not supported: anniversary, assistantname, assistnamephonenumber, children, department, officelocation, radiophonenumber, spouse, rtf - - return $data; - } - - - /** - * Discover all the addressbooks collections for a user under a root. - * - */ - private function discoverAddressbooks() { - unset($this->addressbooks); - $this->addressbooks = array(); - $raw = $this->server->get(false, false, true); - if ($raw !== false) { - $xml = new SimpleXMLElement($raw); - foreach ($xml->addressbook_element as $response) { - if ($this->gal_url !== false) { - if (strcmp(urldecode($response->url), $this->gal_url) == 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Ignoring GAL addressbook '%s'", $this->gal_url)); - continue; - } - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::discoverAddressbooks() Found addressbook '%s'", urldecode($response->url))); - $this->addressbooks[] = urldecode($response->url); - } - unset($xml); - } - } - - /** - * Returns de addressbookId of a vcard. - * The vcardId sent to the device is formed as [addressbookId]-[vcardId] - * - * @param string $vcardId vcard ID in device. - * @return addressbookId - */ - private function getAddressbookIdFromVcard($vcardId) { - $parts = explode("-", $vcardId); - - return $parts[0]; - } - - /** - * Returns de vcard id stored in the carddav server. - * - * @param string $vcardId vcard ID in device - * @return vcard id in carddav server - */ - private function getVcardId($vcardId) { - $parts = explode("-", $vcardId); - - $id = ""; - for ($i = 1; $i < count($parts); $i++) { - if ($i > 1) { - $id .= "-"; - } - $id .= $parts[$i]; - } - - return $id; - } - - /** - * Convert an addressbook url into a zpush id. - * - * @param string $addressbookUrl AddressBook URL - * @return id or false - */ - private function convertAddressbookUrl($addressbookUrl) { - $this->InitializePermanentStorage(); - - // check if this addressbookUrl was converted before - $addressbookId = $this->getAddressbookFromUrl($addressbookUrl); - - // nothing found, so generate a new id and put it in the cache - if ($addressbookId === false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::convertAddressbookUrl('%s') New addressbook", $addressbookUrl)); - // generate addressbookId and add it to the mapping - $addressbookId = sprintf('%04x%04x', mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )); - - // addressbookId to addressbookUrl mapping - if (!isset($this->permanentStorage->fmAidAurl)) - $this->permanentStorage->fmAidAurl = array(); - - $a = $this->permanentStorage->fmAidAurl; - $a[$addressbookId] = $addressbookUrl; - $this->permanentStorage->fmAidAurl = $a; - - // addressbookUrl to addressbookId mapping - if (!isset($this->permanentStorage->fmAurlAid)) - $this->permanentStorage->fmAurlAid = array(); - - $b = $this->permanentStorage->fmAurlAid; - $b[$addressbookUrl] = $addressbookId; - $this->permanentStorage->fmAurlAid = $b; - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::convertAddressbookUrl('%s') = %s", $addressbookUrl, $addressbookId)); - - return $addressbookId; - } - - /** - * Get the URL of an addressbook zpush id. - * - * @param string $addressbookId AddressBook Z-Push based ID - * @return url or false - */ - private function getAddressbookFromId($addressbookId) { - $this->InitializePermanentStorage(); - - $addressbookUrl = false; - - if (isset($this->permanentStorage->fmAidAurl)) { - if (isset($this->permanentStorage->fmAidAurl[$addressbookId])) { - $addressbookUrl = $this->permanentStorage->fmAidAurl[$addressbookId]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, $addressbookUrl)); - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, 'not found')); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromId('%s') = %s", $addressbookId, 'not initialized!')); - } - - return $addressbookUrl; - } - - /** - * Get the zpush id of an addressbook. - * - * @param string $addressbookUrl AddressBook URL - * @return id or false - */ - private function getAddressbookFromUrl($addressbookUrl) { - $this->InitializePermanentStorage(); - - $addressbookId = false; - - if (isset($this->permanentStorage->fmAurlAid)) { - if (isset($this->permanentStorage->fmAurlAid[$addressbookUrl])) { - $addressbookId = $this->permanentStorage->fmAurlAid[$addressbookUrl]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, $addressbookId)); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, 'not found')); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV::getAddressbookFromUrl('%s') = %s", $addressbookUrl, 'not initialized!')); - } - - return $addressbookId; - } - -} \ No newline at end of file diff --git a/sources/backend/carddav/config.php b/sources/backend/carddav/config.php deleted file mode 100644 index b82b5c9..0000000 --- a/sources/backend/carddav/config.php +++ /dev/null @@ -1,109 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ************************ -// BackendCardDAV settings -// ************************ - -// Server protocol: http or https -define('CARDDAV_PROTOCOL', 'https'); - -// Server name -define('CARDDAV_SERVER', 'localhost'); - -// Server port -define('CARDDAV_PORT', '443'); - -// Server path to the addressbook, or the principal with the addressbooks -// If your user has more than 1 addressbook point it to the principal. -// Example: user test@domain.com will have 2 addressbooks -// http://localhost/caldav.php/test@domain.com/addresses/personal -// http://localhost/caldav.php/test@domain.com/addresses/work -// You set the CARDDAV_PATH to '/caldav.php/%u/addresses/' and personal and work will be autodiscovered -// %u: replaced with the username -// %d: replaced with the domain -// Add the trailing / -define('CARDDAV_PATH', '/caldav.php/%u/'); - - -// Server path to the default addressbook -// Mobile device will create new contacts here. It must be under CARDDAV_PATH -// %u: replaced with the username -// %d: replaced with the domain -// Add the trailing / -define('CARDDAV_DEFAULT_PATH', '/caldav.php/%u/addresses/'); - -// Server path to the GAL addressbook. This addressbook is readonly and searchable by the user, but it will NOT be synced. -// If you don't want GAL, comment it -// %u: replaced with the username -// %d: replaced with the domain -// Add the trailing / -define('CARDDAV_GAL_PATH', '/caldav.php/%d/GAL/'); - -// Minimal length for the search pattern to do the real search. -define('CARDDAV_GAL_MIN_LENGTH', 5); - -// Addressbook display name, the name showed in the mobile device -// %u: replaced with the username -// %d: replaced with the domain -define('CARDDAV_CONTACTS_FOLDER_NAME', '%u Addressbook'); - - -// If the CardDAV server supports the sync-collection operation -// DAViCal and SabreDav support it, but Owncloud, SOGo don't -// SabreDav version must be at least 1.9.0, otherwise set this to false -// Setting this to false will work with most servers, but it will be slower: 1 petition for the href of vcards, and 1 petition for each vcard -define('CARDDAV_SUPPORTS_SYNC', false); - - -// If the CardDAV server supports the FN attribute for searches -// DAViCal supports it, but SabreDav, Owncloud and SOGo don't -// Setting this to true will search by FN. If false will search by sn, givenName and email -// It's safe to leave it as false -define('CARDDAV_SUPPORTS_FN_SEARCH', false); - - -// If your carddav server needs to use file extension to recover a vcard. -// Davical needs it -// SOGo official demo online needs it, but some SOGo installation don't need it, so test it -define('CARDDAV_URL_VCARD_EXTENSION', '.vcf'); \ No newline at end of file diff --git a/sources/backend/combined/combined.php b/sources/backend/combined/combined.php deleted file mode 100644 index 28d110e..0000000 --- a/sources/backend/combined/combined.php +++ /dev/null @@ -1,718 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -//include the CombinedBackend's own config file -require_once("backend/combined/config.php"); -require_once("backend/combined/importer.php"); -require_once("backend/combined/exporter.php"); - -class BackendCombined extends Backend implements ISearchProvider { - public $config; - /** - * @var IBackend[] - */ - public $backends; - /** - * @var IBackend - */ - private $activeBackend; - private $activeBackendID; - private $numberChangesSink; - - private $logon_done = false; - - /** - * Constructor of the combined backend - * - * @access public - */ - public function BackendCombined() { - parent::Backend(); - $this->config = BackendCombinedConfig::GetBackendCombinedConfig(); - - $backend_values = array_unique(array_values($this->config['folderbackend'])); - foreach ($backend_values as $i) { - ZPush::IncludeBackend($this->config['backends'][$i]['name']); - $this->backends[$i] = new $this->config['backends'][$i]['name'](); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined %d backends loaded.", count($this->backends))); - } - - /** - * Authenticates the user on each backend - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - */ - public function Logon($username, $domain, $password) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Logon('%s', '%s',***))", $username, $domain)); - if(!is_array($this->backends)){ - return false; - } - foreach ($this->backends as $i => $b){ - $u = $username; - $d = $domain; - $p = $password; - - // Apply mapping from configuration - if(isset($this->config['backends'][$i]['users'])){ - if(!isset($this->config['backends'][$i]['users'][$username])){ - unset($this->backends[$i]); - continue; - } - if(isset($this->config['backends'][$i]['users'][$username]['username'])) - $u = $this->config['backends'][$i]['users'][$username]['username']; - if(isset($this->config['backends'][$i]['users'][$username]['password'])) - $p = $this->config['backends'][$i]['users'][$username]['password']; - if(isset($this->config['backends'][$i]['users'][$username]['domain'])) - $d = $this->config['backends'][$i]['users'][$username]['domain']; - } - - // Apply username mapping from state backend - if (isset($this->config['usemapping']) && $this->config['usemapping']) { - $mappedUsername = ZPush::GetStateMachine()->GetMappedUsername($u, strtolower($this->config['backends'][$i]['name'])); - if ($mappedUsername !== null) { - $u = $mappedUsername; - } - } - - if ($this->backends[$i]->Logon($u, $d, $p) == false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Logon() failed on %s ", $this->config['backends'][$i]['name'])); - return false; - } - $this->backends[$i]->SetOriginalUsername($username); - } - - $this->logon_done = true; - ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logon() success"); - return true; - } - - /** - * Setup the backend to work on a specific store or checks ACLs there. - * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be - * performed on this store (switch operations store). - * If the ACL check is enabled, this operation should just indicate the ACL status on - * the submitted store, without changing the store for operations. - * For the ACL status, the currently logged on user MUST have access rights on - * - the entire store - admin access if no folderid is sent, or - * - on a specific folderid in the store (secretary/full access rights) - * - * The ACLcheck MUST fail if a folder of the authenticated user is checked! - * - * @param string $store target store, could contain a "domain\user" value - * @param boolean $checkACLonly if set to true, Setup() should just check ACLs - * @param string $folderid if set, only ACLs on this folderid are relevant - * - * @access public - * @return boolean - */ - public function Setup($store, $checkACLonly = false, $folderid = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Setup('%s', '%s', '%s')", $store, Utils::PrintAsString($checkACLonly), $folderid)); - if(!is_array($this->backends)){ - return false; - } - foreach ($this->backends as $i => $b){ - $u = $store; - if(isset($this->config['backends'][$i]['users']) && isset($this->config['backends'][$i]['users'][$store]['username'])){ - $u = $this->config['backends'][$i]['users'][$store]['username']; - } - if($this->backends[$i]->Setup($u, $checkACLonly, $folderid) == false){ - ZLog::Write(LOGLEVEL_WARN, "Combined->Setup() failed"); - return false; - } - } - ZLog::Write(LOGLEVEL_DEBUG, "Combined->Setup() success"); - return true; - } - - /** - * Logs off each backend - * - * @access public - * @return boolean - */ - public function Logoff() { - // If no Logon in done, omit Logoff - if (!$this->logon_done) - return true; - - ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logoff()"); - foreach ($this->backends as $i => $b){ - $this->backends[$i]->Logoff(); - } - ZLog::Write(LOGLEVEL_DEBUG, "Combined->Logoff() success"); - return true; - } - - /** - * Returns an array of SyncFolder types with the entire folder hierarchy - * from all backends combined - * - * provides AS 1.0 compatibility - * - * @access public - * @return array SYNC_FOLDER - */ - public function GetHierarchy(){ - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetHierarchy()"); - $ha = array(); - foreach ($this->backends as $i => $b){ - if(!empty($this->config['backends'][$i]['subfolder'])){ - $f = new SyncFolder(); - $f->serverid = $i.$this->config['delimiter'].'0'; - $f->parentid = '0'; - $f->displayname = $this->config['backends'][$i]['subfolder']; - $f->type = SYNC_FOLDER_TYPE_OTHER; - $ha[] = $f; - } - $h = $this->backends[$i]->GetHierarchy(); - if(is_array($h)){ - foreach($h as $j => $f){ - $h[$j]->serverid = $i.$this->config['delimiter'].$h[$j]->serverid; - if($h[$j]->parentid != '0' || !empty($this->config['backends'][$i]['subfolder'])){ - $h[$j]->parentid = $i.$this->config['delimiter'].$h[$j]->parentid; - } - if(isset($this->config['folderbackend'][$h[$j]->type]) && $this->config['folderbackend'][$h[$j]->type] != $i){ - $h[$j]->type = SYNC_FOLDER_TYPE_OTHER; - } - } - $ha = array_merge($ha, $h); - } - } - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetHierarchy() success"); - return $ha; - } - - /** - * Returns the importer to process changes from the mobile - * - * @param string $folderid (opt) - * - * @access public - * @return object(ImportChanges) - */ - public function GetImporter($folderid = false) { - if($folderid !== false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetImporter() Content: ImportChangesCombined:('%s')", $folderid)); - - // get the contents importer from the folder in a backend - // the importer is wrapped to check foldernames in the ImportMessageMove function - $backend = $this->GetBackend($folderid); - if($backend === false) - return false; - $importer = $backend->GetImporter($this->GetBackendFolder($folderid)); - if($importer){ - return new ImportChangesCombined($this, $folderid, $importer); - } - return false; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetImporter() -> Hierarchy: ImportChangesCombined()"); - //return our own hierarchy importer which send each change to the right backend - return new ImportChangesCombined($this); - } - } - - /** - * Returns the exporter to send changes to the mobile - * the exporter from right backend for contents exporter and our own exporter for hierarchy exporter - * - * @param string $folderid (opt) - * - * @access public - * @return object(ExportChanges) - */ - public function GetExporter($folderid = false){ - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetExporter('%s')", $folderid)); - if($folderid){ - $backend = $this->GetBackend($folderid); - if($backend == false) - return false; - return $backend->GetExporter($this->GetBackendFolder($folderid)); - } - return new ExportChangesCombined($this); - } - - /** - * Sends an e-mail - * This messages needs to be saved into the 'sent items' folder - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->SendMail()"); - // Convert source folderid - if (isset($sm->source->folderid)) { - $sm->source->folderid = $this->GetBackendFolder($sm->source->folderid); - } - foreach ($this->backends as $i => $b){ - if($this->backends[$i]->SendMail($sm) == true){ - return true; - } - } - return false; - } - - /** - * Returns all available data of a single message - * - * @param string $folderid - * @param string $id - * @param ContentParameters $contentparameters flag - * - * @access public - * @return object(SyncObject) - * @throws StatusException - */ - public function Fetch($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->Fetch('%s', '%s', CPO)", $folderid, $id)); - $backend = $this->GetBackend($folderid); - if($backend == false) - return false; - return $backend->Fetch($this->GetBackendFolder($folderid), $id, $contentparameters); - } - - /** - * Returns the waste basket - * If the wastebasket is set to one backend, return the wastebasket of that backend - * else return the first waste basket we can find - * - * @access public - * @return string - */ - function GetWasteBasket(){ - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetWasteBasket()"); - - if (isset($this->activeBackend)) { - if (!$this->activeBackend->GetWasteBasket()) - return false; - else - return $this->activeBackendID . $this->config['delimiter'] . $this->activeBackend->GetWasteBasket(); - } - - return false; - } - - /** - * Returns the content of the named attachment as stream. - * There is no way to tell which backend the attachment is from, so we try them all - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->GetAttachmentData('%s')", $attname)); - foreach ($this->backends as $i => $b) { - try { - $attachment = $this->backends[$i]->GetAttachmentData($attname); - if ($attachment instanceof SyncItemOperationsAttachment) - return $attachment; - } - catch (StatusException $s) { - // backends might throw StatusExceptions if it's not their attachment - } - } - throw new StatusException("Combined->GetAttachmentData(): no backend found", SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - /** - * Processes a response to a meeting request. - * - * @param string $requestid id of the object containing the request - * @param string $folderid id of the parent folder of $requestid - * @param string $response - * - * @access public - * @return string id of the created/updated calendar obj - * @throws StatusException - */ - public function MeetingResponse($requestid, $folderid, $response) { - $backend = $this->GetBackend($folderid); - if($backend === false) - return false; - return $backend->MeetingResponse($requestid, $this->GetBackendFolder($folderid), $response); - } - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - // TODO: - return false; - } - - - /** - * Deletes all contents of the specified folder. - * This is generally used to empty the trash (wastebasked), but could also be used on any - * other folder. - * - * @param string $folderid - * @param boolean $includeSubfolders (opt) also delete sub folders, default true - * - * @access public - * @return boolean - * @throws StatusException - */ - public function EmptyFolder($folderid, $includeSubfolders = true) { - $backend = $this->GetBackend($folderid); - if($backend === false) - return false; - return $backend->EmptyFolder($this->GetBackendFolder($folderid), $includeSubfolders); - } - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->HasChangesSink()")); - - $this->numberChangesSink = 0; - - foreach ($this->backends as $i => $b) { - if ($this->backends[$i]->HasChangesSink()) { - $this->numberChangesSink++; - } - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->HasChangesSink - Number ChangesSink found: %d", $this->numberChangesSink)); - - return true; - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBacken->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if there is any problem with that folder - */ - public function ChangesSinkInitialize($folderid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s')", $folderid)); - - $backend = $this->GetBackend($folderid); - if($backend === false) { - // if not backend is found we return true, we don't want this to cause an error - return true; - } - - if ($backend->HasChangesSink()) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSinkInitialize('%s') is supported, initializing", $folderid)); - return $backend->ChangesSinkInitialize($this->GetBackendFolder($folderid)); - } - - // if the backend doesn't support ChangesSink, we also return true so we don't get an error - return true; - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink(%d)", $timeout)); - - $notifications = array(); - if ($this->numberChangesSink == 0) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendCombined doesn't include any Sinkable backends"); - } else { - $time_each = $timeout / $this->numberChangesSink; - foreach ($this->backends as $i => $b) { - if ($this->backends[$i]->HasChangesSink()) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCombined->ChangesSink - Calling in '%s' with %d", get_class($b), $time_each)); - - $notifications_backend = $this->backends[$i]->ChangesSink($time_each); - //preppend backend delimiter - for ($c = 0; $c < count($notifications_backend); $c++) { - $notifications_backend[$c] = $i . $this->config['delimiter'] . $notifications_backend[$c]; - } - $notifications = array_merge($notifications, $notifications_backend); - } - } - } - - return $notifications; - } - - /** - * Finds the correct backend for a folder - * - * @param string $folderid combinedid of the folder - * - * @access public - * @return object - */ - public function GetBackend($folderid){ - $pos = strpos($folderid, $this->config['delimiter']); - if($pos === false) - return false; - $id = substr($folderid, 0, $pos); - if(!isset($this->backends[$id])) - return false; - - $this->activeBackend = $this->backends[$id]; - $this->activeBackendID = $id; - return $this->backends[$id]; - } - - /** - * Returns an understandable folderid for the backend - * - * @param string $folderid combinedid of the folder - * - * @access public - * @return string - */ - public function GetBackendFolder($folderid){ - $pos = strpos($folderid, $this->config['delimiter']); - if($pos === false) - return false; - return substr($folderid,$pos + strlen($this->config['delimiter'])); - } - - /** - * Returns backend id for a folder - * - * @param string $folderid combinedid of the folder - * - * @access public - * @return object - */ - public function GetBackendId($folderid){ - $pos = strpos($folderid, $this->config['delimiter']); - if($pos === false) - return false; - return substr($folderid, 0, $pos); - } - - /** - * Indicates which AS version is supported by the backend. - * Return the lowest version supported by the backends used. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - $version = ZPush::ASV_14; - foreach ($this->backends as $i => $b) { - $subversion = $this->backends[$i]->GetSupportedASVersion(); - if ($subversion < $version) { - $version = $subversion; - } - } - return $version; - } - - /** - * Returns the BackendCombined as it implements the ISearchProvider interface - * This could be overwritten by the global configuration - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider() { - return $this; - } - - - /*----------------------------------------------------------------------------------------- - -- ISearchProvider - ------------------------------------------------------------------------------------------*/ - /** - * Indicates if a search type is supported by this SearchProvider - * It supports all the search types, searches are delegated. - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->SupportsType('%s')", $searchtype)); - $i = $this->getSearchBackend($searchtype); - - return $i !== false; - } - - - /** - * Queries the LDAP backend - * - * @param string $searchquery string to be searched for - * @param string $searchrange specified searchrange - * - * @access public - * @return array search results - */ - public function GetGALSearchResults($searchquery, $searchrange) { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetGALSearchResults()"); - $i = $this->getSearchBackend(ISearchProvider::SEARCH_GAL); - - $result = false; - if ($i !== false) { - $result = $this->backends[$i]->GetGALSearchResults($searchquery, $searchrange); - } - - return $result; - } - - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo) { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->GetMailboxSearchResults()"); - $i = $this->getSearchBackend(ISearchProvider::SEARCH_MAILBOX); - - $result = false; - if ($i !== false) { - //Convert $cpo GetSearchFolderid - $cpo->SetSearchFolderid($this->GetBackendFolder($cpo->GetSearchFolderid())); - $result = $this->backends[$i]->GetMailboxSearchResults($cpo, $i . $this->config['delimiter']); - } - - return $result; - } - - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->TerminateSearch()"); - foreach ($this->backends as $i => $b) { - if ($this->backends[$i] instanceof ISearchProvider) { - $this->backends[$i]->TerminateSearch($pid); - } - } - - return true; - } - - - /** - * Disconnects backends - * - * @access public - * @return boolean - */ - public function Disconnect() { - ZLog::Write(LOGLEVEL_DEBUG, "Combined->Disconnect()"); - foreach ($this->backends as $i => $b) { - if ($this->backends[$i] instanceof ISearchProvider) { - $this->backends[$i]->Disconnect(); - } - } - - return true; - } - - - /** - * Returns the first backend that support a search type - * - * @param string $searchtype - * - * @access private - * @return string - */ - private function getSearchBackend($searchtype) { - foreach ($this->backends as $i => $b) { - if ($this->backends[$i] instanceof ISearchProvider) { - if ($this->backends[$i]->SupportsType($searchtype)) { - return $i; - } - } - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Combined->getSearchBackend('%s') No support found!", $searchtype)); - - return false; - } -} diff --git a/sources/backend/combined/config.php b/sources/backend/combined/config.php deleted file mode 100644 index ca6bd16..0000000 --- a/sources/backend/combined/config.php +++ /dev/null @@ -1,116 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class BackendCombinedConfig { - - // ************************* - // BackendCombined settings - // ************************* - /** - * Returns the configuration of the combined backend - * - * @access public - * @return array - * - */ - public static function GetBackendCombinedConfig() { - //use a function for it because php does not allow - //assigning variables to the class members (expecting T_STRING) - return array( - //the order in which the backends are loaded. - //login only succeeds if all backend return true on login - //sending mail: the mail is sent with first backend that is able to send the mail - 'backends' => array( - 'i' => array( - 'name' => 'BackendIMAP', - ), - 'z' => array( - 'name' => 'BackendZarafa', - ), - 'm' => array( - 'name' => 'BackendMaildir', - ), - 'v' => array( - 'name' => 'BackendVCardDir', - ), - 'c' => array( - 'name' => 'BackendCalDAV', - ), - 'l' => array( - 'name' => 'BackendLDAP', - ), - 'd' => array( - 'name' => 'BackendCardDAV', - ), - ), - 'delimiter' => '/', - //force one type of folder to one backend - //it must match one of the above defined backends - 'folderbackend' => array( - SYNC_FOLDER_TYPE_INBOX => 'i', - SYNC_FOLDER_TYPE_DRAFTS => 'i', - SYNC_FOLDER_TYPE_WASTEBASKET => 'i', - SYNC_FOLDER_TYPE_SENTMAIL => 'i', - SYNC_FOLDER_TYPE_OUTBOX => 'i', - SYNC_FOLDER_TYPE_TASK => 'z', - SYNC_FOLDER_TYPE_APPOINTMENT => 'z', - SYNC_FOLDER_TYPE_CONTACT => 'z', - SYNC_FOLDER_TYPE_NOTE => 'z', - SYNC_FOLDER_TYPE_JOURNAL => 'z', - SYNC_FOLDER_TYPE_OTHER => 'i', - SYNC_FOLDER_TYPE_USER_MAIL => 'i', - SYNC_FOLDER_TYPE_USER_APPOINTMENT => 'z', - SYNC_FOLDER_TYPE_USER_CONTACT => 'z', - SYNC_FOLDER_TYPE_USER_TASK => 'z', - SYNC_FOLDER_TYPE_USER_JOURNAL => 'z', - SYNC_FOLDER_TYPE_USER_NOTE => 'z', - SYNC_FOLDER_TYPE_UNKNOWN => 'z', - ), - //creating a new folder in the root folder should create a folder in one backend - 'rootcreatefolderbackend' => 'i', - //enable to use username mapping for the different backends - 'usemapping' => false, - ); - } -} diff --git a/sources/backend/combined/exporter.php b/sources/backend/combined/exporter.php deleted file mode 100644 index eb1abbd..0000000 --- a/sources/backend/combined/exporter.php +++ /dev/null @@ -1,189 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -/** - * the ExportChangesCombined class is returned from GetExporter for changes. - * It combines the changes from all backends and prepends all folderids with the backendid - */ - -class ExportChangesCombined implements IExportChanges { - /** - * @var BackendCombined - */ - private $backend; - private $syncstates; - /** - * @var IExportChanges[] - */ - private $exporters; - private $importer; - private $importwraps; - - public function ExportChangesCombined(&$backend) { - $this->backend =& $backend; - $this->exporters = array(); - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined constructed"); - } - - /** - * Initializes the state and flags - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean status flag - */ - public function Config($syncstate, $flags = 0) { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Config(...)"); - $this->syncstates = $syncstate; - if(!is_array($this->syncstates)){ - $this->syncstates = array(); - } - - foreach($this->backend->backends as $i => $b){ - if(isset($this->syncstates[$i])){ - $state = $this->syncstates[$i]; - } else { - $state = ''; - } - - $this->exporters[$i] = $this->backend->backends[$i]->GetExporter(); - $this->exporters[$i]->Config($state, $flags); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Config() success"); - } - - /** - * Returns the amount of changes to be exported - * - * @access public - * @return int - */ - public function GetChangeCount() { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetChangeCount()"); - $c = 0; - foreach($this->exporters as $i => $e){ - $c += $this->exporters[$i]->GetChangeCount(); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetChangeCount() success"); - return $c; - } - - /** - * Synchronizes a change to the configured importer - * - * @access public - * @return array with status information - */ - public function Synchronize() { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Synchronize()"); - foreach($this->exporters as $i => $e){ - if(!empty($this->backend->config['backends'][$i]['subfolder']) && !isset($this->syncstates[$i])){ - // first sync and subfolder backend - $f = new SyncFolder(); - $f->serverid = $i.$this->backend->config['delimiter'].'0'; - $f->parentid = '0'; - $f->displayname = $this->backend->config['backends'][$i]['subfolder']; - $f->type = SYNC_FOLDER_TYPE_OTHER; - $this->importer->ImportFolderChange($f); - } - while(is_array($this->exporters[$i]->Synchronize())); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->Synchronize() success"); - return true; - } - - /** - * Reads and returns the current state - * - * @access public - * @return string - */ - public function GetState() { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetState()"); - foreach($this->exporters as $i => $e){ - $this->syncstates[$i] = $this->exporters[$i]->GetState(); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->GetState() success"); - return $this->syncstates; - } - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->ConfigContentParameters()"); - foreach($this->exporters as $i => $e){ - //call the ConfigContentParameters() of each exporter - $e->ConfigContentParameters($contentparameters); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->ConfigContentParameters() success"); - } - - /** - * Sets the importer where the exporter will sent its changes to - * This exporter should also be ready to accept calls after this - * - * @param object &$importer Implementation of IImportChanges - * - * @access public - * @return boolean - */ - public function InitializeExporter(&$importer) { - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->InitializeExporter(...)"); - foreach ($this->exporters as $i => $e) { - if(!isset($this->importwraps[$i])){ - $this->importwraps[$i] = new ImportHierarchyChangesCombinedWrap($i, $this->backend, $importer); - } - $e->InitializeExporter($this->importwraps[$i]); - } - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesCombined->InitializeExporter(...) success"); - } -} diff --git a/sources/backend/combined/importer.php b/sources/backend/combined/importer.php deleted file mode 100644 index 1c01826..0000000 --- a/sources/backend/combined/importer.php +++ /dev/null @@ -1,360 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ImportChangesCombined implements IImportChanges { - private $backend; - private $folderid; - private $icc; - - /** - * Constructor of the ImportChangesCombined class - * - * @param object $backend - * @param string $folderid - * @param object $importer - * - * @access public - */ - public function ImportChangesCombined(&$backend, $folderid = false, $icc = false) { - $this->backend = $backend; - $this->folderid = $folderid; - $this->icc = &$icc; - } - - /** - * Loads objects which are expected to be exported with the state - * Before importing/saving the actual message from the mobile, a conflict detection should be done - * - * @param ContentParameters $contentparameters class of objects - * @param string $state - * - * @access public - * @return boolean - * @throws StatusException - */ - public function LoadConflicts($contentparameters, $state) { - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->LoadConflicts() icc not configured"); - return false; - } - $this->icc->LoadConflicts($contentparameters, $state); - } - - /** - * Imports a single message - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean/string failure / id of message - */ - public function ImportMessageChange($id, $message) { - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageChange() icc not configured"); - return false; - } - return $this->icc->ImportMessageChange($id, $message); - } - - /** - * Imports a deletion. This may conflict if the local object has been modified - * - * @param string $id - * - * @access public - * @return boolean - */ - public function ImportMessageDeletion($id) { - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageDeletion() icc not configured"); - return false; - } - return $this->icc->ImportMessageDeletion($id); - } - - /** - * Imports a change in 'read' flag - * This can never conflict - * - * @param string $id - * @param int $flags - * - * @access public - * @return boolean - */ - public function ImportMessageReadFlag($id, $flags) { - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageReadFlag() icc not configured"); - return false; - } - return $this->icc->ImportMessageReadFlag($id, $flags); - } - - - /** - * Imports a move of a message. This occurs when a user moves an item to another folder - * - * @param string $id - * @param string $newfolder - * - * @access public - * @return boolean - */ - public function ImportMessageMove($id, $newfolder) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove('%s', '%s')", $id, $newfolder)); - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ImportMessageMove icc not configured"); - return false; - } - if($this->backend->GetBackendId($this->folderid) != $this->backend->GetBackendId($newfolder)){ - ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportMessageMove() cannot move message between two backends"); - return false; - } - $res = $this->icc->ImportMessageMove($id, $this->backend->GetBackendFolder($newfolder)); - - if ($res) { - //TODO: we should add newid to new folder, instead of a full folder resync - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportMessageMove(): Force resync of dest folder (%s)", $newfolder)); - ZPushAdmin::ResyncFolder(Request::GetAuthUser(), Request::GetDeviceID(), $newfolder); - } - - return $res; - } - - - /**---------------------------------------------------------------------------------------------------------- - * Methods to import hierarchy - */ - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return boolean/string status/id of the folder - */ - public function ImportFolderChange($folder) { - $id = $folder->serverid; - $parent = $folder->parentid; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportFolderChange() id: '%s', parent: '%s'", $id, $parent)); - if($parent == '0') { - if($id) { - $backendid = $this->backend->GetBackendId($id); - } - else { - $backendid = $this->backend->config['rootcreatefolderbackend']; - } - } - else { - $backendid = $this->backend->GetBackendId($parent); - $parent = $this->backend->GetBackendFolder($parent); - } - - if(!empty($this->backend->config['backends'][$backendid]['subfolder']) && $id == $backendid.$this->backend->config['delimiter'].'0') { - ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderChange() cannot change static folder"); - return false; - } - - if($id != false) { - if($backendid != $this->backend->GetBackendId($id)) { - ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderChange() cannot move folder between two backends"); - return false; - } - $id = $this->backend->GetBackendFolder($id); - } - - $this->icc = $this->backend->getBackend($backendid)->GetImporter(); - $res = $this->icc->ImportFolderChange($folder); - ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->ImportFolderChange() success'); - return $backendid.$this->backend->config['delimiter'].$res; - } - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent id - * - * @access public - * @return boolean/int success/SYNC_FOLDERHIERARCHY_STATUS - */ - public function ImportFolderDeletion($id, $parent = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesCombined->ImportFolderDeletion('%s', '%s'), $id, $parent")); - $backendid = $this->backend->GetBackendId($id); - if(!empty($this->backend->config['backends'][$backendid]['subfolder']) && $id == $backendid.$this->backend->config['delimiter'].'0') { - ZLog::Write(LOGLEVEL_WARN, "ImportChangesCombined->ImportFolderDeletion() cannot change static folder"); - return false; //we can not change a static subfolder - } - - $backend = $this->backend->GetBackend($id); - $id = $this->backend->GetBackendFolder($id); - - if($parent != '0') - $parent = $this->backend->GetBackendFolder($parent); - - $this->icc = $backend->GetImporter(); - $res = $this->icc->ImportFolderDeletion($id, $parent); - ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->ImportFolderDeletion() success'); - return $res; - } - - - /** - * Initializes the state and flags - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean status flag - */ - public function Config($state, $flags = 0) { - ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->Config(...)'); - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->Config() icc not configured"); - return false; - } - $this->icc->Config($state, $flags); - ZLog::Write(LOGLEVEL_DEBUG, 'ImportChangesCombined->Config() success'); - } - - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesCombined->ConfigContentParameters()"); - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->ConfigContentParameters() icc not configured"); - return false; - } - $this->icc->ConfigContentParameters($contentparameters); - ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesCombined->ConfigContentParameters() success"); - } - - /** - * Reads and returns the current state - * - * @access public - * @return string - */ - public function GetState() { - if (!$this->icc) { - ZLog::Write(LOGLEVEL_ERROR, "ImportChangesCombined->GetState() icc not configured"); - return false; - } - return $this->icc->GetState(); - } -} - - -/** - * The ImportHierarchyChangesCombinedWrap class wraps the importer given in ExportChangesCombined->Config. - * It prepends the backendid to all folderids and checks foldertypes. - */ - -class ImportHierarchyChangesCombinedWrap { - private $ihc; - private $backend; - private $backendid; - - /** - * Constructor of the ImportChangesCombined class - * - * @param string $backendid - * @param object $backend - * @param object $ihc - * - * @access public - */ - public function ImportHierarchyChangesCombinedWrap($backendid, &$backend, &$ihc) { - ZLog::Write(LOGLEVEL_DEBUG, "ImportHierarchyChangesCombinedWrap->ImportHierarchyChangesCombinedWrap('$backendid',...)"); - $this->backendid = $backendid; - $this->backend =& $backend; - $this->ihc = &$ihc; - } - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return boolean/string status/id of the folder - */ - public function ImportFolderChange($folder) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportHierarchyChangesCombinedWrap->ImportFolderChange('%s')", $folder->serverid)); - $folder->serverid = $this->backendid.$this->backend->config['delimiter'].$folder->serverid; - if($folder->parentid != '0' || !empty($this->backend->config['backends'][$this->backendid]['subfolder'])){ - $folder->parentid = $this->backendid.$this->backend->config['delimiter'].$folder->parentid; - } - if(isset($this->backend->config['folderbackend'][$folder->type]) && $this->backend->config['folderbackend'][$folder->type] != $this->backendid){ - ZLog::Write(LOGLEVEL_DEBUG, sprintf("not using folder: '%s' ('%s')", $folder->displayname, $folder->serverid)); - return true; - } - ZLog::Write(LOGLEVEL_DEBUG, "ImportHierarchyChangesCombinedWrap->ImportFolderChange() success"); - return $this->ihc->ImportFolderChange($folder); - } - - /** - * Imports a folder deletion - * - * @param string $id - * - * @access public - * - * @return boolean/int success/SYNC_FOLDERHIERARCHY_STATUS - */ - public function ImportFolderDeletion($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportHierarchyChangesCombinedWrap->ImportFolderDeletion('%s')", $id)); - return $this->ihc->ImportFolderDeletion($this->backendid.$this->backend->config['delimiter'].$id); - } -} diff --git a/sources/backend/imap/README b/sources/backend/imap/README deleted file mode 100644 index ab6c342..0000000 --- a/sources/backend/imap/README +++ /dev/null @@ -1,28 +0,0 @@ -BackendIMAP - NOTES -=================== - -This backend support the Search operation in the mailbox. -Since the IMAP search operation is pretty slow, with a medium/big mailbox, or with a lots of folders, -the mobile device will timeout the operation before this is completed on server. - -I'm using Dovecot + FTS-SOLR plugin so the real search is done against an Apache SOLR server. -It reduces a 1-2 minutes search to 1-5 seconds, and the response is given to the mobile device in time. - - -CHANGESSINK -=========== -It supports ChangesSink method that will detect and send faster changes to your device. - - -SMTP -==== -You can choice between 3 methods for send mails: mail (php mail), sendmail (native binary), smtp (php smtp direct connection). -Remember to configure it in the config.php - -"mail" is a sendmail wrapper in Linux. - - -MBCONVERT -========= -A lot of messages come with wrong encoding, to them to look better with any device you can pre-convert them to UTF-8. -You will need to install the php-mbstring module \ No newline at end of file diff --git a/sources/backend/imap/REQUIREMENTS b/sources/backend/imap/REQUIREMENTS deleted file mode 100644 index 740bdae..0000000 --- a/sources/backend/imap/REQUIREMENTS +++ /dev/null @@ -1,7 +0,0 @@ -REQUIREMENTS: -php-imap -php-mbstring (optional but recommended) -libawl-php - - -IMAP server (Dovecot, Courier...) \ No newline at end of file diff --git a/sources/backend/imap/THANKS b/sources/backend/imap/THANKS deleted file mode 100644 index b56e45c..0000000 --- a/sources/backend/imap/THANKS +++ /dev/null @@ -1,4 +0,0 @@ -*Drenalina SRL (www.drenalina.com)* sponsored the development of the following features in the BackendIMAP, any existing bug it's my fault not theirs ;-) -Thank you very much for helping to improve it!! - - - Meeting invitations and attendees \ No newline at end of file diff --git a/sources/backend/imap/config.php b/sources/backend/imap/config.php deleted file mode 100644 index 8f00310..0000000 --- a/sources/backend/imap/config.php +++ /dev/null @@ -1,227 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ************************ -// BackendIMAP settings -// ************************ - -// Defines the server to which we want to connect -define('IMAP_SERVER', 'localhost'); - -// connecting to default port (143) -define('IMAP_PORT', 143); - -// best cross-platform compatibility (see http://php.net/imap_open for options) -define('IMAP_OPTIONS', '/notls/norsh'); - - -// Mark messages as read when moving to Trash. -// BE AWARE that you will lose the unread flag, but some mail clients do this so the Trash folder doesn't get boldened -define('IMAP_AUTOSEEN_ON_DELETE', false); - - -// IMPORTANT: BASIC IMAP FOLDERS [ask your mail admin] - // We can have diferent cases (case insensitive): - // 1. - // inbox - // sent - // drafts - // trash - // 2. - // inbox - // common.sent - // common.drafts - // common.trash - // 3. - // common.inbox - // common.sent - // common.drafts - // common.trash - // 4. - // common - // common.sent - // common.drafts - // common.trash - // - // gmail is a special case, where the default folders are under the [gmail] prefix and the folders defined by the user are under INBOX. - // This configuration seems to work: - // define('IMAP_FOLDER_PREFIX', ''); - // define('IMAP_FOLDER_INBOX', 'INBOX'); - // define('IMAP_FOLDER_SENT', '[Gmail]/Sent'); - // define('IMAP_FOLDER_DRAFTS', '[Gmail]/Drafts'); - // define('IMAP_FOLDER_TRASH', '[Gmail]/Trash'); - // define('IMAP_FOLDER_SPAM', '[Gmail]/Spam'); - // define('IMAP_FOLDER_ARCHIVE', '[Gmail]/All Mail'); - -// Since I know you won't configure this, I will raise an error unless you do. -// When configured set this to true to remove the error -define('IMAP_FOLDER_CONFIGURED', false); - -// Folder prefix is the common part in your names (3, 4) -define('IMAP_FOLDER_PREFIX', ''); - -// Inbox will have the preffix preppend (3 & 4 to true) -define('IMAP_FOLDER_PREFIX_IN_INBOX', false); - -// Inbox folder name (case doesn't matter) - (empty in 4) -define('IMAP_FOLDER_INBOX', 'INBOX'); - -// Sent folder name (case doesn't matter) -define('IMAP_FOLDER_SENT', 'SENT'); - -// Draft folder name (case doesn't matter) -define('IMAP_FOLDER_DRAFT', 'DRAFTS'); - -// Trash folder name (case doesn't matter) -define('IMAP_FOLDER_TRASH', 'TRASH'); - -// Spam folder name (case doesn't matter). Only showed as special by iOS devices -define('IMAP_FOLDER_SPAM', 'SPAM'); - -// Archive folder name (case doesn't matter). Only showed as special by iOS devices -define('IMAP_FOLDER_ARCHIVE', 'ARCHIVE'); - - - -// forward messages inline (default true - inlined) -define('IMAP_INLINE_FORWARD', true); - -// list of folders we want to exclude from sync. Names, or part of it, separated by | -// example: dovecot.sieve|archive|spam -define('IMAP_EXCLUDED_FOLDERS', ''); - - - -// overwrite the "from" header with some value -// options: -// '' - do nothing, use the From header -// 'username' - the username will be set (usefull if your login is equal to your emailaddress) -// 'domain' - the value of the "domain" field is used -// 'sql' - the username will be the result of a sql query. REMEMBER TO INSTALL PHP-PDO AND PHP-DATABASE -// 'ldap' - the username will be the result of a ldap query. REMEMBER TO INSTALL PHP-LDAP!! -// '@mydomain.com' - the username is used and the given string will be appended -define('IMAP_DEFAULTFROM', ''); - -// DSN: formatted PDO connection string -// mysql:host=xxx;port=xxx;dbname=xxx -// USER: username to DB -// PASSWORD: password to DB -// OPTIONS: array with options needed -// QUERY: query to execute -// FIELDS: columns in the query -// FROM: string that will be the from, replacing the column names with the values -define('IMAP_FROM_SQL_DSN', ''); -define('IMAP_FROM_SQL_USER', ''); -define('IMAP_FROM_SQL_PASSWORD', ''); -define('IMAP_FROM_SQL_OPTIONS', serialize(array(PDO::ATTR_PERSISTENT => true))); -define('IMAP_FROM_SQL_QUERY', "select first_name, last_name, mail_address from users where mail_address = '#username@#domain'"); -define('IMAP_FROM_SQL_FIELDS', serialize(array('first_name', 'last_name', 'mail_address'))); -define('IMAP_FROM_SQL_FROM', '#first_name #last_name <#mail_address>'); -define('IMAP_FROM_SQL_FULLNAME', '#first_name #last_name'); - -// SERVER: ldap server -// SERVER_PORT: ldap port -// USER: dn to use for connecting -// PASSWORD: password -// QUERY: query to execute -// FIELDS: columns in the query -// FROM: string that will be the from, replacing the field names with the values -define('IMAP_FROM_LDAP_SERVER', 'localhost'); -define('IMAP_FROM_LDAP_SERVER_PORT', '389'); -define('IMAP_FROM_LDAP_USER', 'cn=zpush,ou=servers,dc=zpush,dc=org'); -define('IMAP_FROM_LDAP_PASSWORD', 'password'); -define('IMAP_FROM_LDAP_BASE', 'dc=zpush,dc=org'); -define('IMAP_FROM_LDAP_QUERY', '(mail=#username@#domain)'); -define('IMAP_FROM_LDAP_FIELDS', serialize(array('givenname', 'sn', 'mail'))); -define('IMAP_FROM_LDAP_FROM', '#givenname #sn <#mail>'); -define('IMAP_FROM_LDAP_FULLNAME', '#givenname #sn'); - - - -// Method used for sending mail -// mail => mail() php function -// sendmail => sendmail executable -// smtp => direct connection against SMTP -define('IMAP_SMTP_METHOD', 'mail'); - -global $imap_smtp_params; -// SMTP Parameters -// mail : no params -$imap_smtp_params = array(); -// sendmail -//$imap_smtp_params = array('sendmail_path' => '/usr/bin/sendmail', 'sendmail_args' => '-i'); -// smtp -// "host" - The server to connect. Default is localhost. -// "port" - The port to connect. Default is 25. -// "auth" - Whether or not to use SMTP authentication. Default is FALSE. -// "username" - The username to use for SMTP authentication. "imap_username" for using the same username as the imap server -// "password" - The password to use for SMTP authentication. "imap_password" for using the same password as the imap server -// "localhost" - The value to give when sending EHLO or HELO. Default is localhost -// "timeout" - The SMTP connection timeout. Default is NULL (no timeout). -// "verp" - Whether to use VERP or not. Default is FALSE. -// "debug" - Whether to enable SMTP debug mode or not. Default is FALSE. -// "persist" - Indicates whether or not the SMTP connection should persist over multiple calls to the send() method. -// "pipelining" - Indicates whether or not the SMTP commands pipelining should be used. -// "verify_peer" - Require verification of SSL certificate used. Default is TRUE. -// "verify_peer_name" - Require verification of peer name. Default is TRUE. -// "allow_self_signed" - Allow self-signed certificates. Requires verify_peer. Default is FALSE. -//$imap_smtp_params = array('host' => 'localhost', 'port' => 25, 'auth' => false); -// If you want to use SSL with port 25 or port 465 you must preppend "ssl://" before the hostname or IP of your SMTP server -// IMPORTANT: To use SSL you must use PHP 5.1 or later, install openssl libs and use ssl:// within the host variable -// IMPORTANT: To use SSL with PHP 5.6 you should set verify_peer, verify_peer_name and allow_self_signed -//$imap_smtp_params = array('host' => 'ssl://localhost', 'port' => 465, 'auth' => true, 'username' => 'imap_username', 'password' => 'imap_password'); - - - -// If you are using IMAP_SMTP_METHOD = mail or sendmail and your sent messages are not correctly displayed you can change this to "\n". -// BUT, it doesn't comply with RFC 2822 and will break if using smtp method -define('MAIL_MIMEPART_CRLF', "\r\n"); - - -// A file containing file mime types->extension mappings. -// SELINUX users: make sure the file has a security context accesible by your apache/php-fpm process -define('SYSTEM_MIME_TYPES_MAPPING', '/etc/mime.types'); - - -// Use BackendCalDAV for Meetings. You cannot hope to get that functionality working without a caldav backend. -define('IMAP_MEETING_USE_CALDAV', false); \ No newline at end of file diff --git a/sources/backend/imap/imap.php b/sources/backend/imap/imap.php deleted file mode 100644 index 3889f05..0000000 --- a/sources/backend/imap/imap.php +++ /dev/null @@ -1,2707 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/imap/config.php"); - -require_once("backend/imap/mime_calendar.php"); -require_once("backend/imap/mime_encode.php"); -require_once("backend/imap/user_identity.php"); - -class BackendIMAP extends BackendDiff implements ISearchProvider { - private $wasteID; - private $sentID; - private $server; - private $mbox; - private $mboxFolder; - private $username; - private $password; - private $domain; - private $sinkfolders = array(); - private $sinkstates = array(); - private $changessinkinit = false; - private $folderhierarchy; - private $excludedFolders; - private static $mimeTypes = false; - - - public function BackendIMAP() { - if (BackendIMAP::$mimeTypes === false) { - BackendIMAP::$mimeTypes = $this->SystemExtensionMimeTypes(); - } - $this->wasteID = false; - $this->sentID = false; - $this->mboxFolder = ""; - - if (!function_exists("imap_open")) - throw new FatalException("BackendIMAP(): php-imap module is not installed", 0, null, LOGLEVEL_FATAL); - - if (!function_exists("mb_detect_order")) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP(): php-mbstring module is not installed, you should install it for better encoding conversions")); - } - } - - /**---------------------------------------------------------------------------------------------------------- - * default backend methods - */ - - /** - * Authenticates the user - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - * @throws FatalException if php-imap module can not be found - */ - public function Logon($username, $domain, $password) { - $this->wasteID = false; - $this->sentID = false; - $this->server = "{" . IMAP_SERVER . ":" . IMAP_PORT . "/imap" . IMAP_OPTIONS . "}"; - - if (!function_exists("imap_open")) - throw new FatalException("BackendIMAP(): php-imap module is not installed", 0, null, LOGLEVEL_FATAL); - - if (defined('IMAP_FOLDER_CONFIGURED') && IMAP_FOLDER_CONFIGURED == false) - throw new FatalException("BackendIMAP(): You didn't configure your IMAP folder names. Do it before!", 0, null, LOGLEVEL_FATAL); - - /* BEGIN fmbiete's contribution r1527, ZP-319 */ - $this->excludedFolders = array(); - if (defined('IMAP_EXCLUDED_FOLDERS') && strlen(IMAP_EXCLUDED_FOLDERS) > 0) { - $this->excludedFolders = explode("|", IMAP_EXCLUDED_FOLDERS); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->Logon(): Excluding Folders (%s)", IMAP_EXCLUDED_FOLDERS)); - } - /* END fmbiete's contribution r1527, ZP-319 */ - - // open the IMAP-mailbox - $this->mbox = @imap_open($this->server , $username, $password, OP_HALFOPEN); - $this->mboxFolder = ""; - - if ($this->mbox) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->Logon(): User '%s' is authenticated on '%s'", $username, $this->server)); - $this->username = $username; - $this->password = $password; - $this->domain = $domain; - return true; - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendIMAP->Logon(): can't connect as user '%s' on '%s': %s", $username, $this->server, imap_last_error())); - return false; - } - } - - /** - * Logs off - * Called before shutting down the request to close the IMAP connection - * writes errors to the log - * - * @access public - * @return boolean - */ - public function Logoff() { - $this->close_connection(); - $this->SaveStorages(); - } - - /** - * Sends an e-mail - * This messages needs to be saved into the 'sent items' folder - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", - strlen($sm->mime), - Utils::PrintAsString($sm->forwardflag ? (isset($sm->source->itemid) ? $sm->source->itemid : "error no itemid") : false), - Utils::PrintAsString($sm->replyflag ? (isset($sm->source->itemid) ? $sm->source->itemid : "error no itemid") : false), - Utils::PrintAsString((isset($sm->source->folderid) ? $sm->source->folderid : false)), - Utils::PrintAsString(($sm->saveinsent)), Utils::PrintAsString(isset($sm->replacemime)))); - - // by splitting the message in several lines we can easily grep later - foreach(preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) - ZLog::Write(LOGLEVEL_WBXML, "RFC822: ". $rfc822line); - - $sourceMessage = $sourceMail = false; - // If we have a reference to a source message and we are not replacing mime (since we wouldn't use it) - if (isset($sm->source->folderid) && isset($sm->source->itemid) && (!isset($sm->replacemime) || $sm->replacemime === false)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We have a source message and we try to fetch it")); - $parent = $this->getImapIdFromFolderId($sm->source->folderid); - if ($parent === false) { - throw new StatusException(sprintf("BackendIMAP->SendMail(): Could not get imapid from source folderid '%'", $sm->source->folderid), SYNC_COMMONSTATUS_ITEMNOTFOUND); - } - else { - $this->imap_reopen_folder($parent); - $sourceMail = @imap_fetchheader($this->mbox, $sm->source->itemid, FT_UID) . @imap_body($this->mbox, $sm->source->itemid, FT_PEEK | FT_UID); - $mobj = new Mail_mimeDecode($sourceMail); - $sourceMessage = $mobj->decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); - unset($mobj); - //We will need $sourceMail if the message is forwarded and not inlined - - // If it's a reply, we mark the original message as answered - if ($sm->replyflag) { - if (!@imap_setflag_full($this->mbox, $sm->source->itemid, "\\Answered", ST_UID)) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->SendMail(): Unable to mark the message as Answered")); - } - } - - // If it's a forward, we mark the original message as forwarded - if ($sm->forwardflag) { - if (!@imap_setflag_full($this->mbox, $sm->source->itemid, "\\Forwarded", ST_UID)) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->SendMail(): Unable to mark the message as Forwarded")); - } - } - } - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the new message")); - $mobj = new Mail_mimeDecode($sm->mime); - $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); - unset($mobj); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the From and To")); - $Mail_RFC822 = new Mail_RFC822(); - - $toaddr = ""; - $this->setFromHeaderValue($message->headers); - $fromaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["from"])); - - if (isset($message->headers["to"])) { - $toaddr = $this->parseAddr($Mail_RFC822->parseAddressList($message->headers["to"])); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): To defined: %s", $toaddr)); - } - unset($Mail_RFC822); - - // overwrite CC and BCC with the decoded versions, because we will parse/validate the address in the sending method - if (isset($message->headers["cc"])) { - $message->headers["cc"] = $message->headers["cc"]; - } - if (isset($message->headers["bcc"])) { - $message->headers["bcc"] = $message->headers["bcc"]; - } - - $this->setReturnPathValue($message->headers, $fromaddr); - - $finalBody = ""; - $finalHeaders = array(); - - // if it's a S/MIME message or has VCALENDAR objects I don't do anything with it - if (is_smime($message) || has_calendar_object($message)) { - $mobj = new Mail_mimeDecode($sm->mime); - $parts = $mobj->getSendArray(); - unset($mobj); - if ($parts === false) { - throw new StatusException(sprintf("BackendIMAP->SendMail(): Could not getSendArray for SMIME messages"), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); - } - else { - list($recipients, $finalHeaders, $finalBody) = $parts; - - $this->setFromHeaderValue($finalHeaders); - $this->setReturnPathValue($finalHeaders, $fromaddr); - } - } - else { - //http://pear.php.net/manual/en/package.mail.mail-mime.example.php - //http://pear.php.net/manual/en/package.mail.mail-mimedecode.decode.php - //http://pear.php.net/manual/en/package.mail.mail-mimepart.addsubpart.php - - // I don't mind if the new message is multipart or not, I always will create a multipart. It's simpler - $finalEmail = new Mail_mimePart('', array('content_type' => 'multipart/mixed')); - - if ($sm->replyflag && (!isset($sm->replacemime) || $sm->replacemime === false)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Replying message")); - $this->addTextParts($finalEmail, $message, $sourceMessage, true); - - if (isset($message->parts)) { - // We add extra parts from the replying message - add_extra_sub_parts($finalEmail, $message->parts); - } - // A replied message doesn't include the original attachments - } - else if ($sm->forwardflag && (!isset($sm->replacemime) || $sm->replacemime === false)) { - if (!defined('IMAP_INLINE_FORWARD') || IMAP_INLINE_FORWARD === false) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Forwarding message as attached file - eml"); - $finalEmail->addSubPart($sourceMail, array('content_type' => 'message/rfc822', 'encoding' => 'base64', 'disposition' => 'attachment', 'dfilename' => 'forwarded_message.eml')); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Forwarding inlined message"); - $this->addTextParts($finalEmail, $message, $sourceMessage, false); - - if (isset($message->parts)) { - // We add extra parts from the forwarding message - add_extra_sub_parts($finalEmail, $message->parts); - } - if (isset($sourceMessage->parts)) { - // We add extra parts from the forwarded message - add_extra_sub_parts($finalEmail, $sourceMessage->parts); - } - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): is a new message or we are replacing mime")); - $this->addTextPartsMessage($finalEmail, $message); - if (isset($message->parts)) { - // We add extra parts from the new message - add_extra_sub_parts($finalEmail, $message->parts); - } - } - - // We encode the final message - $boundary = '=_' . md5(rand() . microtime()); - $finalEmail = $finalEmail->encode($boundary); - - $finalHeaders = array('Mime-Version' => '1.0'); - // We copy all the non-existent headers, minus content_type - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Copying new headers")); - foreach ($message->headers as $k => $v) { - if (strcasecmp($k, 'content-type') != 0 && strcasecmp($k, 'content-transfer-encoding') != 0 && strcasecmp($k, 'mime-version') != 0) { - if (!isset($finalHeaders[$k])) - $finalHeaders[ucwords($k)] = $v; - } - } - foreach ($finalEmail['headers'] as $k => $v) { - if (!isset($finalHeaders[$k])) - $finalHeaders[$k] = $v; - } - - $finalBody = "This is a multi-part message in MIME format.\n" . $finalEmail['body']; - - unset($finalEmail); - } - - unset($sourceMail); - unset($message); - unset($sourceMessage); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): Final mail to send:")); - foreach ($finalHeaders as $k => $v) - ZLog::Write(LOGLEVEL_WBXML, sprintf("%s: %s", $k, $v)); - foreach (preg_split("/((\r)?\n)/", $finalBody) as $bodyline) - ZLog::Write(LOGLEVEL_WBXML, sprintf("Body: %s", $bodyline)); - - $send = $this->sendMessage($fromaddr, $toaddr, $finalHeaders, $finalBody); - - if ($send) { - if (isset($sm->saveinsent)) { - $this->saveSentMessage($finalHeaders, $finalBody); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->SendMail(): Not saving in SentFolder"); - } - } - - unset($finalHeaders); - unset($finalBody); - - return $send; - } - - /** - * Add text parts to a mimepart object, with reply or forward tags - * - * @param Mail_mimePart $email reference to the object - * @param Mail_mimeDecode $message reference to the message - * @param Mail_mimeDecode $sourceMessage reference to the original message - * @param boolean $isReply true if it's a reply, false if it's a forward - * - * @access private - * @return void - */ - private function addTextParts(&$email, &$message, &$sourceMessage, $isReply = true) { - $htmlBody = $plainBody = ''; - Mail_mimeDecode::getBodyRecursive($message, "html", $htmlBody); - Mail_mimeDecode::getBodyRecursive($message, "plain", $plainBody); - $htmlSource = $plainSource = ''; - Mail_mimeDecode::getBodyRecursive($sourceMessage, "html", $htmlSource); - Mail_mimeDecode::getBodyRecursive($sourceMessage, "plain", $plainSource); - - $separator = ''; - if ($isReply) { - $separator = ">\r\n"; - $separatorHtml = "
"; - $separatorHtmlEnd = "
"; - } - else { - $separator = ""; - $separatorHtml = "
"; - $separatorHtmlEnd = "
"; - } - - $altEmail = new Mail_mimePart('', array('content_type' => 'multipart/alternative')); - - if (strlen($htmlBody) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The message has HTML body")); - if (strlen($htmlSource) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The original message had HTML body")); - $altEmail->addSubPart($htmlBody . $separatorHtml . $htmlSource . $separatorHtmlEnd, array('content_type' => 'text/html; charset=utf-8', 'encoding' => 'base64')); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The original message had not HTML body, we use original PLAIN body to create HTML")); - $altEmail->addSubPart($htmlBody . $separatorHtml . "

" . $plainSource . "

" . $separatorHtmlEnd, array('content_type' => 'text/html; charset=utf-8', 'encoding' => 'base64')); - } - } - if (strlen($plainBody) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The message has PLAIN body")); - if (strlen($plainSource) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The original message had PLAIN body")); - $altEmail->addSubPart($plainBody . $separator . str_replace("\n", "\n> ", "> " . $plainSource), array('content_type' => 'text/plain; charset=utf-8', 'encoding' => 'base64')); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextParts(): The original message had not PLAIN body, we use original HTML body to create PLAIN")); - $altEmail->addSubPart($plainBody . $separator . str_replace("\n", "\n> ", "> " . Utils::ConvertHtmlToText($htmlSource)), array('content_type' => 'text/plain; charset=utf-8', 'encoding' => 'base64')); - } - } - - $boundary = '=_' . md5(rand() . microtime()); - $altEmail = $altEmail->encode($boundary); - - $email->addSubPart($altEmail['body'], array('content_type' => 'multipart/alternative;'."\n".' boundary="'.$boundary.'"')); - - unset($altEmail); - - unset($htmlBody); - unset($htmlSource); - unset($plainBody); - unset($plainSource); - } - - /** - * Add text parts to a mimepart object - * - * @param Mail_mimePart $email reference to the object - * @param Mail_mimeDecode $message reference to the message - * - * @access private - * @return void - */ - private function addTextPartsMessage(&$email, &$message) { - $altEmail = new Mail_mimePart('', array('content_type' => 'multipart/alternative')); - - foreach (array("plain", "html", "calendar") as $type) { - $body = ''; - Mail_mimeDecode::getBodyRecursive($message, $type, $body); - if (strlen($body) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->addTextPartsMessage(): The message has %s body", $type)); - $altEmail->addSubPart($body, array('content_type' => sprintf("text/%s; charset=utf-8", $type), 'encoding' => 'base64')); - } - } - unset($body); - - $boundary = '=_' . md5(rand() . microtime()); - $altEmail = $altEmail->encode($boundary); - - $email->addSubPart($altEmail['body'], array('content_type' => 'multipart/alternative;'."\n".' boundary="'.$boundary.'"')); - - unset($altEmail); - } - - /** - * Returns the waste basket - * - * @access public - * @return string - */ - public function GetWasteBasket() { - // TODO this could be retrieved from the DeviceFolderCache - if ($this->wasteID == false) { - //try to get the waste basket without doing complete hierarchy sync - $folder_name = IMAP_FOLDER_TRASH; - if (defined('IMAP_FOLDER_PREFIX') && strlen(IMAP_FOLDER_PREFIX) > 0) - $folder_name = IMAP_FOLDER_PREFIX . $this->getServerDelimiter() . $folder_name; - $wastebaskt = @imap_getmailboxes($this->mbox, $this->server, $folder_name); - if (isset($wastebaskt[0])) { - $this->wasteID = $this->convertImapId(substr($wastebaskt[0]->name, strlen($this->server))); - return $this->wasteID; - } - //try get waste id from hierarchy if it wasn't possible with above for some reason - $this->GetHierarchy(); - } - return $this->wasteID; - } - - /** - * Returns the content of the named attachment as stream. The passed attachment identifier is - * the exact string that is returned in the 'AttName' property of an SyncAttachment. - * Any information necessary to find the attachment must be encoded in that 'attname' property. - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetAttachmentData('%s')", $attname)); - - list($folderid, $id, $part) = explode(":", $attname); - - if (!isset($folderid) || !isset($id) || !isset($part)) - throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, attachment name key can not be parsed", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - // convert back to work on an imap-id - $folderImapid = $this->getImapIdFromFolderId($folderid); - - $this->imap_reopen_folder($folderImapid); - $mail = @imap_fetchheader($this->mbox, $id, FT_UID) . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); - - if (empty($mail)) { - throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, message not found, maybe was moved", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - $mobj = new Mail_mimeDecode($mail); - $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); - - if (!isset($message->parts)) { - throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, message without parts. Requesting part key: '%d'", $attname, $part), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - /* BEGIN fmbiete's contribution r1528, ZP-320 */ - //trying parts - $mparts = $message->parts; - for ($i = 0; $i < count($mparts); $i++) { - $auxpart = $mparts[$i]; - //recursively add parts - if($auxpart->ctype_primary == "multipart" && ($auxpart->ctype_secondary == "mixed" || $auxpart->ctype_secondary == "alternative" || $auxpart->ctype_secondary == "related")) { - foreach($auxpart->parts as $spart) - $mparts[] = $spart; - } - } - /* END fmbiete's contribution r1528, ZP-320 */ - - if (!isset($mparts[$part]->body)) - throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, requested part key can not be found: '%d'", $attname, $part), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - // unset mimedecoder & mail - unset($mobj); - unset($mail); - - $attachment = new SyncItemOperationsAttachment(); - /* BEGIN fmbiete's contribution r1528, ZP-320 */ - $attachment->data = StringStreamWrapper::Open($mparts[$part]->body); - if (isset($mparts[$part]->ctype_primary) && isset($mparts[$part]->ctype_secondary)) - $attachment->contenttype = $mparts[$part]->ctype_primary .'/'.$mparts[$part]->ctype_secondary; - - unset($mparts); - unset($message); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetAttachmentData contenttype %s", $attachment->contenttype)); - /* END fmbiete's contribution r1528, ZP-320 */ - - return $attachment; - } - - - /** - * Deletes all contents of the specified folder. - * This is generally used to empty the trash (wastebasked), but could also be used on any - * other folder. - * - * @param string $folderid - * @param boolean $includeSubfolders (opt) also delete sub folders, default true - * - * @access public - * @return boolean - * @throws StatusException - */ - public function EmptyFolder($folderid, $includeSubfolders = true) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->EmptyFolder('%s', '%s')", $folderid, Utils::PrintAsString($includeSubfolders))); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - if ($folderImapid === false) { - throw new StatusException(sprintf("BackendIMAP->EmptyFolder('%s','%s'): Error, unable to open folder (no entry id)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - } - - if (!$this->imap_reopen_folder($folderImapid)) { - throw new StatusException(sprintf("BackendIMAP->EmptyFolder('%s','%s'): Error, unable to open parent folder (open entry)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->EmptyFolder('%s','%s'): emptying folder", $folderid, Utils::PrintAsString($includeSubfolders))); - - // TODO: make transactional all these deletes: see comment bellow - if (@imap_delete($this->mbox, "1:*")) { - @imap_expunge($this->mbox); - - - // An error erasing any subfolder won't return an error to the device, because we should undelete the already expunged messages, and we cannot undelete a folder - if ($includeSubfolders) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->EmptyFolder('%s','%s'): deleting subfolders", $folderid, Utils::PrintAsString($includeSubfolders))); - - // Find subfolders - $subfolders = @imap_getmailboxes($this->mbox, $this->server . $folderImapid, "*"); - if (is_array($subfolders)) { - - // delete mailbox and its content - foreach ($subfolders as $val) { - $subname = substr($val->name, strlen($this->server)); - if (!@imap_deletemailbox($this->mbox, $val->name)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendIMAP->EmptyFolder('%s','%s'): Error deleting subfolder %s", $folderid, Utils::PrintAsString($includeSubfolders), $subname)); - } - } - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendIMAP->EmptyFolder('%s','%s'): Error getting subfolder list", $folderid, Utils::PrintAsString($includeSubfolders))); - } - } - } - else { - throw new StatusException(sprintf("BackendIMAP->EmptyFolder('%s','%s'): Error, imap_delete() failed, the error will show at the logout", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - } - - return true; - } - - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * The IMAP backend simulates a sink by polling status information of the folder - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - return true; - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBacken->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if found can not be found - */ - public function ChangesSinkInitialize($folderid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->ChangesSinkInitialize(): folderid '%s'", $folderid)); - - $imapid = $this->getImapIdFromFolderId($folderid); - - if (!$this->changessinkinit) { - // First folder, store the actual folder structure - $this->folderhierarchy = $this->get_folder_list(); - } - - if ($imapid !== false) { - $this->sinkfolders[] = $imapid; - $this->changessinkinit = true; - } - - return $this->changessinkinit; - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - $notifications = array(); - $stopat = time() + $timeout - 1; - - //We can get here and the ChangesSink not be initialized yet - if (!$this->changessinkinit) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP>ChangesSink - Not initialized ChangesSink, sleep and exit")); - // We sleep and do nothing else - sleep($timeout); - return $notifications; - } - - // Reconnect IMAP server - $this->imap_reconnect(); - - // Check folder hierarchy and create change - if (count(array_diff($this->folderhierarchy, $this->get_folder_list())) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->ChangesSink(): Changes in folder hierarchy detected!!"); - throw new StatusException("BackendIMAP->ChangesSink(): HierarchySync required.", SyncCollections::HIERARCHY_CHANGED); - } - - // only check once to reduce pressure in the IMAP server - foreach ($this->sinkfolders as $i => $imapid) { - $this->imap_reopen_folder($imapid); - - // courier-imap only clears the status cache after checking - @imap_check($this->mbox); - - $status = @imap_status($this->mbox, $this->server . $imapid, SA_ALL); - if (!$status) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ChangesSink: could not stat folder '%s': %s ", $this->getFolderIdFromImapId($imapid), imap_last_error())); - } - else { - $newstate = "M:". $status->messages ."-R:". $status->recent ."-U:". $status->unseen; - - if (! isset($this->sinkstates[$imapid]) ) { - $this->sinkstates[$imapid] = $newstate; - } - - if ($this->sinkstates[$imapid] != $newstate) { - $notifications[] = $this->getFolderIdFromImapId($imapid); - $this->sinkstates[$imapid] = $newstate; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->ChangesSink(): ChangesSink detected!!"); - } - } - } - // Close IMAP connection, we will reconnect in the next execution. This will reduce IMAP pressure - $this->close_connection(); - - // Wait to timeout - if (empty($notifications)) { - while ($stopat > time()) { - sleep(1); - } - } - - return $notifications; - } - - - /**---------------------------------------------------------------------------------------------------------- - * implemented DiffBackend methods - */ - - - /** - * Returns a list (array) of folders. - * - * @access public - * @return array/boolean false if the list could not be retrieved - */ - public function GetFolderList() { - $folders = array(); - - $list = $this->get_folder_list(); - foreach ($list as $val) { - // don't return the excluded folders - $notExcluded = true; - for ($i = 0, $cnt = count($this->excludedFolders); $notExcluded && $i < $cnt; $i++) { // expr1, expr2 modified by mku ZP-329 - // fix exclude folders with special chars by mku ZP-329 - if (strpos(strtolower($val), strtolower(Utils::Utf7_iconv_encode(Utils::Utf8_to_utf7($this->excludedFolders[$i])))) !== false) { - $notExcluded = false; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Pattern: <%s> found, excluding folder: '%s'", $this->excludedFolders[$i], $val)); // sprintf added by mku ZP-329 - } - } - - if ($notExcluded) { - $box = array(); - // cut off serverstring - $imapid = substr($val, strlen($this->server)); - $box["id"] = $this->convertImapId($imapid); - - $folders[] = $box; - } - } - - return $folders; - } - - /** - * Returns an actual SyncFolder object - * - * @param string $id id of the folder - * - * @access public - * @return object SyncFolder with information - */ - public function GetFolder($id) { - $folder = new SyncFolder(); - $folder->serverid = $id; - - // convert back to work on an imap-id - $imapid = $this->getImapIdFromFolderId($id); - - // explode hierarchy - $fhir = explode($this->getServerDelimiter(), $imapid); - - // TODO WasteID or SentID could be saved for later ussage - if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_INBOX)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Inbox"; - $folder->type = SYNC_FOLDER_TYPE_INBOX; - } - else if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_DRAFT)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Drafts"; - $folder->type = SYNC_FOLDER_TYPE_DRAFTS; - } - else if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_SENT)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Sent"; - $folder->type = SYNC_FOLDER_TYPE_SENTMAIL; - $this->sentID = $id; - } - else if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_TRASH)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Trash"; - $folder->type = SYNC_FOLDER_TYPE_WASTEBASKET; - $this->wasteID = $id; - } - else if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_SPAM)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Junk"; - $folder->type = SYNC_FOLDER_TYPE_USER_MAIL; - } - else if (strcasecmp($imapid, $this->create_name_folder(IMAP_FOLDER_ARCHIVE)) == 0) { - $folder->parentid = "0"; - $folder->displayname = "Archive"; - $folder->type = SYNC_FOLDER_TYPE_USER_MAIL; - } - else { - if (defined('IMAP_FOLDER_PREFIX') && strlen(IMAP_FOLDER_PREFIX) > 0) { - if (strcasecmp($fhir[0], IMAP_FOLDER_PREFIX) == 0) { - // Discard prefix - array_shift($fhir); - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->GetFolder('%s'): '%s'; using server delimiter '%s', first part '%s' is not equal to the prefix defined '%s'. Something is wrong with your config.", $id, $imapid, $this->getServerDelimiter(), $fhir[0], IMAP_FOLDER_PREFIX)); - } - } - - if (count($fhir) == 1) { - $folder->displayname = Utils::Utf7_to_utf8(Utils::Utf7_iconv_decode($fhir[0])); - $folder->parentid = "0"; - } - else { - $this->getModAndParentNames($fhir, $folder->displayname, $imapparent); - $folder->displayname = Utils::Utf7_to_utf8(Utils::Utf7_iconv_decode($folder->displayname)); - if ($imapparent === null) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->GetFolder('%s'): '%s'; we didn't found a valid parent name for the folder, but we should... contact the developers for further info", $id, $imapid)); - $folder->parentid = "0"; // We put the folder as root folder, so we see it - } - else { - $folder->parentid = $this->convertImapId($imapparent); - } - } - $folder->type = SYNC_FOLDER_TYPE_USER_MAIL; - } - - //advanced debugging - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetFolder('%s'): '%s'", $id, $folder)); - - return $folder; - } - - /** - * Returns folder stats. An associative array with properties is expected. - * - * @param string $id id of the folder - * - * @access public - * @return array - */ - public function StatFolder($id) { - $folder = $this->GetFolder($id); - - $stat = array(); - $stat["id"] = $id; - $stat["parent"] = $folder->parentid; - $stat["mod"] = $folder->displayname; - - return $stat; - } - - /** - * Creates or modifies a folder - * The folder type is ignored in IMAP, as all folders are Email folders - * - * @param string $folderid id of the parent folder - * @param string $oldid if empty -> new folder created, else folder is to be renamed - * @param string $displayname new folder name (to be created, or to be renamed to) - * @param int $type folder type - * - * @access public - * @return boolean status - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function ChangeFolder($folderid, $oldid, $displayname, $type){ - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendIMAP->ChangeFolder('%s','%s','%s','%s')", $folderid, $oldid, $displayname, $type)); - - // if $id is set => rename mailbox, otherwise create - if ($oldid) { - // rename doesn't work properly with IMAP - // the activesync client doesn't support a 'changing ID' - // TODO this would be solved by implementing hex ids (Mantis #459) - //$csts = imap_renamemailbox($this->mbox, $this->server . imap_utf7_encode(str_replace(".", $this->getServerDelimiter(), $oldid)), $newname); - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->ChangeFolder() : we do not support rename for now"); - return false; - } - else { - - // build name for new mailboxBackendMaildir - $displayname = Utils::Utf7_iconv_encode(Utils::Utf8_to_utf7($displayname)); - - if ($folderid == "0") { - $newimapid = $displayname; - } - else { - $imapid = $this->getImapIdFromFolderId($folderid); - $newimapid = $imapid . $this->getServerDelimiter() . $displayname; - } - - $csts = imap_createmailbox($this->mbox, $this->server . $newimapid); - if ($csts) { - imap_subscribe($this->mbox, $this->server . $newimapid); - return $this->StatFolder($folderid . $this->getServerDelimiter() . $displayname); - } - else { - ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->ChangeFolder() : mailbox creation failed"); - return false; - } - } - } - - /** - * Deletes a folder - * - * @param string $id - * @param string $parent is normally false - * - * @access public - * @return boolean status - false if e.g. does not exist - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function DeleteFolder($id, $parentid){ - $imapid = $this->getImapIdFromFolderId($id); - if ($imapid) { - return imap_deletemailbox($this->mbox, $this->server.$imapid); - } - - return false; - } - - /** - * Returns a list (array) of messages - * - * @param string $folderid id of the parent folder - * @param long $cutoffdate timestamp in the past from which on messages should be returned - * - * @access public - * @return array/false array with messages or false if folder is not available - */ - public function GetMessageList($folderid, $cutoffdate) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessageList('%s','%s')", $folderid, $cutoffdate)); - - $folderid = $this->getImapIdFromFolderId($folderid); - - if ($folderid == false) - throw new StatusException("Folderid not found in cache", SYNC_STATUS_FOLDERHIERARCHYCHANGED); - - $messages = array(); - $this->imap_reopen_folder($folderid, true); - - $sequence = "1:*"; - if ($cutoffdate > 0) { - $search = @imap_search($this->mbox, "SINCE ". date("d-M-Y", $cutoffdate)); - if ($search === false) { - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendIMAP->GetMessageList('%s','%s'): 0 result for the search or error: %s", $folderid, $cutoffdate, imap_last_error())); - return $messages; - } - - $sequence = implode(",", $search); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessageList(): searching with sequence '%s'", $sequence)); - $overviews = @imap_fetch_overview($this->mbox, $sequence); - - if (!is_array($overviews)) { - $error = imap_last_error(); - if (strlen($error) > 0 && imap_num_msg($this->mbox) > 0) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->GetMessageList('%s','%s'): Failed to retrieve overview: %s", $folderid, $cutoffdate, imap_last_error())); - } - return $messages; - } - - foreach ($overviews as $overview) { - // Determine the message's date and apply the cutoff; if the overview's ->udate property is - // not available, fall back to the "Date:" header as it appears in the email. - $date = 0; - if (isset($overview->udate)) { - $date = $overview->udate; - } else if (isset($overview->date)) { - $date = $this->cleanupDate($overview->date); - } - if ($date < $cutoffdate) { - // Message is out of range; ignore it - continue; - } - - // cut of deleted messages - if (isset($overview->deleted) && $overview->deleted) - continue; - - if (isset($overview->uid)) { - $message = array(); - $message["mod"] = $date; - $message["id"] = $overview->uid; - - // 'seen' aka 'read' - if (isset($overview->seen) && $overview->seen) { - $message["flags"] = 1; - } - else { - $message["flags"] = 0; - } - - // 'flagged' aka 'FollowUp' aka 'starred' - if (isset($overview->flagged) && $overview->flagged) { - $message["star"] = 1; - } - else { - $message["star"] = 0; - } - - $messages[] = $message; - } - } - return $messages; - } - - /** - * Returns the actual SyncXXX object type. - * - * @param string $folderid id of the parent folder - * @param string $id id of the message - * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) - * - * @access public - * @return object/false false if the message could not be retrieved - */ - public function GetMessage($folderid, $id, $contentparameters) { - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - $mimesupport = $contentparameters->GetMimeSupport(); - $bodypreference = $contentparameters->GetBodyPreference(); /* fmbiete's contribution r1528, ZP-320 */ - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage('%s', '%s', '%s')", $folderid, $id, implode(",", $bodypreference))); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - - $is_sent_folder = strcasecmp($folderImapid, $this->create_name_folder(IMAP_FOLDER_SENT)) == 0; - - // Get flags, etc - $stat = $this->StatMessage($folderid, $id); - - if ($stat) { - $this->imap_reopen_folder($folderImapid); - $mail_headers = @imap_fetchheader($this->mbox, $id, FT_UID); - $mail = $mail_headers . @imap_body($this->mbox, $id, FT_PEEK | FT_UID); - - if (empty($mail)) { - throw new StatusException(sprintf("BackendIMAP->GetMessage(): Error, message not found, maybe was moved"), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - $mobj = new Mail_mimeDecode($mail); - $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); - - $is_multipart = is_multipart($message); - $is_smime = is_smime($message); - $is_encrypted = $is_smime ? is_encrypted($message) : false; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): Message is multipart: %d, smime: %d, smime encrypted: %d", $is_multipart, $is_smime, $is_encrypted)); - - //Select body type preference - $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; - if ($bodypreference !== false) { - $bpReturnType = Utils::GetBodyPreferenceBestMatch($bodypreference); // changed by mku ZP-330 - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): getBodyPreferenceBestMatch: %d", $bpReturnType)); - - // Prefered format is MIME -OR- message is SMIME -OR- the device supports MIME (iPhone) and doesn't really understand HTML - if ($bpReturnType == SYNC_BODYPREFERENCE_MIME || $is_smime || in_array(SYNC_BODYPREFERENCE_MIME, $bodypreference)) { - $bpReturnType = SYNC_BODYPREFERENCE_MIME; - } - - // We need the text body even though MIME is used, for the preview - $textBody = ""; - Mail_mimeDecode::getBodyRecursive($message, "html", $textBody, true); - if (strlen($textBody) > 0) { - if ($bpReturnType != SYNC_BODYPREFERENCE_MIME) { - $bpReturnType = SYNC_BODYPREFERENCE_HTML; - } - } - else { - Mail_mimeDecode::getBodyRecursive($message, "plain", $textBody, true); - if ($bpReturnType != SYNC_BODYPREFERENCE_MIME) { - $bpReturnType = SYNC_BODYPREFERENCE_PLAIN; - } - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): after thinking a bit we will use: %d", $bpReturnType)); - - - $output = new SyncMail(); - - if (Request::GetProtocolVersion() >= 12.0) { - $output->asbody = new SyncBaseBody(); - - switch($bpReturnType) { - case SYNC_BODYPREFERENCE_PLAIN: - $output->asbody->data = $textBody; - break; - case SYNC_BODYPREFERENCE_HTML: - $output->asbody->data = $textBody; - break; - case SYNC_BODYPREFERENCE_MIME: - if ($is_smime) { - if ($is_encrypted) { - // #190, KD 2015-06-04 - If message body is encrypted only send the headers, as data should only be in the attachment - $output->asbody->data = $mail_headers; - } - else { - $output->asbody->data = $mail; - } - } - else { - $output->asbody->data = build_mime_message($message); - } - break; - case SYNC_BODYPREFERENCE_RTF: - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->GetMessage(): RTF Format NOT CHECKED"); - $output->asbody->data = base64_encode($textBody); - break; - } - - // truncate body, if requested. - // MIME should not be truncated, but encrypted messages are truncated always to the headers size - if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { - if ($is_encrypted) { - $output->asbody->truncated = 1; - } - else { - $output->asbody->truncated = 0; - } - } - else { - if (strlen($output->asbody->data) > $truncsize) { - $output->asbody->data = Utils::Utf8_truncate($output->asbody->data, $truncsize); - $output->asbody->truncated = 1; - } - else { - $output->asbody->truncated = 0; - } - } - - $output->asbody->type = $bpReturnType; - if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { - // NativeBodyType can be only (1 => PLAIN, 2 => HTML, 3 => RTF). MIME uses 1 - $output->nativebodytype = SYNC_BODYPREFERENCE_PLAIN; - } - else { - $output->nativebodytype = $bpReturnType; - } - $output->asbody->estimatedDataSize = strlen($output->asbody->data); - - $bpo = $contentparameters->BodyPreference($output->asbody->type); - if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { - // Preview must be always plaintext - $previewText = ""; - Mail_mimeDecode::getBodyRecursive($message, "plain", $previewText, true); - if (strlen($previewText) == 0) { - Mail_mimeDecode::getBodyRecursive($message, "html", $previewText, true); - $previewText = Utils::ConvertHtmlToText($previewText); - } - $output->asbody->preview = Utils::Utf8_truncate($previewText, $bpo->GetPreview()); - } - } - /* END fmbiete's contribution r1528, ZP-320 */ - else { // ASV_2.5 - //DEPRECATED : very old devices, and incomplete code - - $output->bodytruncated = 0; - /* BEGIN fmbiete's contribution r1528, ZP-320 */ - if ($bpReturnType == SYNC_BODYPREFERENCE_MIME) { - // truncate body, if requested, but never truncate MIME messages - $output->mimetruncated = 0; - $output->mimedata = $mail; - $output->mimesize = strlen($output->mimedata); - } - else { - // truncate body, if requested - if (strlen($textBody) > $truncsize) { - $output->body = Utils::Utf8_truncate($textBody, $truncsize); - $output->bodytruncated = 1; - } - else { - $output->body = $textBody; - $output->bodytruncated = 0; - } - $output->bodysize = strlen($output->body); - } - /* END fmbiete's contribution r1528, ZP-320 */ - } - - unset($textBody); - unset($mail_headers); - - $output->datereceived = isset($message->headers["date"]) ? $this->cleanupDate($message->headers["date"]) : null; - - if ($is_smime) { - // #190, KD 2015-06-04 - Add Encrypted (and possibly signed) to the classifications emitted - if ($is_encrypted) { - $output->messageclass = "IPM.Note.SMIME"; - } - else { - $output->messageclass = "IPM.Note.SMIME.MultipartSigned"; - } - } - else { - $output->messageclass = "IPM.Note"; - } - $output->subject = isset($message->headers["subject"]) ? $message->headers["subject"] : ""; - $output->read = $stat["flags"]; - $output->from = isset($message->headers["from"]) ? $message->headers["from"] : null; - - /* BEGIN fmbiete's contribution r1528, ZP-320 */ - if (isset($message->headers["thread-topic"])) { - $output->threadtopic = $message->headers["thread-topic"]; - /* - //FIXME: Conversation support, get conversationid and conversationindex good values - if (Request::GetProtocolVersion() >= 14.0) { - // since the conversationid must be unique for a thread we could use the threadtopic in base64 minus the == - $output->conversationid = strtoupper(str_replace("=", "", base64_encode($output->threadtopic))); - if (isset($message->headers["thread-index"])) - $output->conversationindex = strtoupper($message->headers["thread-index"]); - } - */ - } - else { - $output->threadtopic = $output->subject; - } - - // Language Code Page ID: http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx - $output->internetcpid = INTERNET_CPID_UTF8; - if (Request::GetProtocolVersion() >= 12.0) { - $output->contentclass = "urn:content-classes:message"; - - $output->flag = new SyncMailFlags(); - if (isset($stat["star"]) && $stat["star"]) { - //flagstatus 0: clear, 1: complete, 2: active - $output->flag->flagstatus = SYNC_FLAGSTATUS_ACTIVE; - //flagtype: for follow up - $output->flag->flagtype = "FollowUp"; - } - else { - $output->flag->flagstatus = SYNC_FLAGSTATUS_CLEAR; - } - } - /* END fmbiete's contribution r1528, ZP-320 */ - - $Mail_RFC822 = new Mail_RFC822(); - $toaddr = $ccaddr = $replytoaddr = array(); - if(isset($message->headers["to"])) - $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); - if(isset($message->headers["cc"])) - $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); - if(isset($message->headers["reply-to"])) - $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply-to"]); - - $output->to = array(); - $output->cc = array(); - $output->reply_to = array(); - foreach(array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { - if ($addrlist === false) { - //If we couldn't parse the addresslist we put the raw header (decoded) - if ($type == "reply_to") { - array_push($output->$type, $message->headers["reply-to"]); - } - else { - array_push($output->$type, $message->headers[$type]); - } - } - else { - foreach($addrlist as $addr) { - // If the address was a group we have "groupname" and "addresses" atributes - if (isset($addr->addresses)) { - if (count($addr->addresses) == 0) { - // readd the empty group delimiter - array_push($output->$type, sprintf("%s:;", $addr->groupname)); - if (!isset($output->displayto) && strlen($addr->groupname) > 0) { - $output->displayto = $addr->groupname; - } - } - else { - foreach($addr->addresses as $addr_group) { - $name = $this->add_address_to_list($output->$type, $addr_group); - if (!isset($output->displayto) && strlen($name) > 0) { - $output->displayto = $name; - } - } - } - } - else { - // Not a group - $name = $this->add_address_to_list($output->$type, $addr); - if (!isset($output->displayto) && strlen($name) > 0) { - $output->displayto = $name; - } - } - } - } - } - - // convert mime-importance to AS-importance - if (isset($message->headers["x-priority"])) { - $mimeImportance = preg_replace("/\D+/", "", $message->headers["x-priority"]); - //MAIL 1 - most important, 3 - normal, 5 - lowest - //AS 0 - low, 1 - normal, 2 - important - if ($mimeImportance > 3) - $output->importance = 0; - elseif ($mimeImportance == 3) - $output->importance = 1; - elseif ($mimeImportance < 3) - $output->importance = 2; - } - else { /* fmbiete's contribution r1528, ZP-320 */ - $output->importance = 1; - } - - // Attachments are also needed for MIME messages - if(isset($message->parts)) { - $mparts = $message->parts; - for ($i=0; $i < count($mparts); $i++) { - $part = $mparts[$i]; - - //recursively add subparts to later processing - if ((isset($part->ctype_primary) && $part->ctype_primary == "multipart") && (isset($part->ctype_secondary) && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related"))) { - if (isset($part->parts)) { - foreach($part->parts as $spart) - $mparts[] = $spart; - } - // Go to the for again - continue; - } - - if (is_calendar($part)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): text/calendar part found, trying to convert")); - $output->meetingrequest = new SyncMeetingRequest(); - parse_meeting_calendar($part, $output, $is_sent_folder); - } - else { - //add part as attachment if it's disposition indicates so or if it is not a text part - if ((isset($part->disposition) && ($part->disposition == "attachment" || $part->disposition == "inline")) || - (isset($part->ctype_primary) && $part->ctype_primary != "text")) { - - if (isset($part->d_parameters['filename'])) - $attname = $part->d_parameters['filename']; - else if (isset($part->ctype_parameters['name'])) - $attname = $part->ctype_parameters['name']; - else if (isset($part->headers['content-description'])) - $attname = $part->headers['content-description']; - else $attname = "unknown attachment"; - - /* BEGIN fmbiete's contribution r1528, ZP-320 */ - if (Request::GetProtocolVersion() >= 12.0) { - if (!isset($output->asattachments) || !is_array($output->asattachments)) - $output->asattachments = array(); - - $attachment = new SyncBaseAttachment(); - - $attachment->estimatedDataSize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; - - $attachment->displayname = $attname; - $attachment->filereference = $folderid . ":" . $id . ":" . $i; - $attachment->method = 1; //Normal attachment - $attachment->contentid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; - if (isset($part->disposition) && $part->disposition == "inline") { - $attachment->isinline = 1; - // #209 - KD 2015-06-16 If we got a filename use it, otherwise guess - if (!isset($part->filename)) { - // We try to fix the name for the inline file. - // FIXME: This is a dirty hack as the used in the Zarafa backend, if you have a better method let me know! - if (isset($part->ctype_primary) && isset($part->ctype_secondary)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): Guessing extension for inline attachment [primary_type %s secondary_type %s]", $part->ctype_primary, $part->ctype_secondary)); - if (isset(BackendIMAP::$mimeTypes[$part->ctype_primary.'/'.$part->ctype_secondary])) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): primary_type %s secondary_type %s", $part->ctype_primary, $part->ctype_secondary)); - $attachment->displayname = "inline_".$i.".".BackendIMAP::$mimeTypes[$part->ctype_primary.'/'.$part->ctype_secondary]; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): no extension found in '%s'!!", SYSTEM_MIME_TYPES_MAPPING)); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMessage(): no primary_type or secondary_type")); - } - } - } - else { - $attachment->isinline = 0; - } - - array_push($output->asattachments, $attachment); - } - else { //ASV_2.5 - if (!isset($output->attachments) || !is_array($output->attachments)) - $output->attachments = array(); - - $attachment = new SyncAttachment(); - - $attachment->attsize = isset($part->d_parameters['size']) ? $part->d_parameters['size'] : isset($part->body) ? strlen($part->body) : 0; - - $attachment->displayname = $attname; - $attachment->attname = $folderid . ":" . $id . ":" . $i; - $attachment->attmethod = 1; - $attachment->attoid = isset($part->headers['content-id']) ? str_replace("<", "", str_replace(">", "", $part->headers['content-id'])) : ""; - - array_push($output->attachments, $attachment); - } - /* END fmbiete's contribution r1528, ZP-320 */ - } - } - } - } - - unset($message); - unset($mobj); - unset($mail); - - return $output; - } - - return false; - } - - /** - * Returns message stats, analogous to the folder stats from StatFolder(). - * - * @param string $folderid id of the folder - * @param string $id id of the message - * - * @access public - * @return array/boolean - */ - public function StatMessage($folderid, $id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->StatMessage('%s','%s')", $folderid, $id)); - $folderImapid = $this->getImapIdFromFolderId($folderid); - - $this->imap_reopen_folder($folderImapid); - $overview = @imap_fetch_overview($this->mbox, $id, FT_UID); - - if (!$overview) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->StatMessage('%s','%s'): Failed to retrieve overview: %s", $folderid, $id, imap_last_error())); - return false; - } - - // without uid it's not a valid message - if (empty($overview[0]->uid)) return false; - - $entry = array(); - if (isset($overview[0]->udate)) { - $entry["mod"] = $overview[0]->udate; - } else if (isset($overview[0]->date)) { - $entry["mod"] = $this->cleanupDate($overview[0]->date); - } else { - $entry["mod"] = 0; - } - $entry["id"] = $overview[0]->uid; - - // 'seen' aka 'read' - if (isset($overview[0]->seen) && $overview[0]->seen) { - $entry["flags"] = 1; - } - else { - $entry["flags"] = 0; - } - - // 'flagged' aka 'FollowUp' aka 'starred' - if (isset($overview[0]->flagged) && $overview[0]->flagged) { - $entry["star"] = 1; - } - else { - $entry["star"] = 0; - } - - return $entry; - } - - /** - * Called when a message has been changed on the mobile. - * Added support for FollowUp flag - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param SyncXXX $message the SyncObject containing a message - * @param ContentParameters $contentparameters - * - * @access public - * @return array same return value as StatMessage() - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function ChangeMessage($folderid, $id, $message, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->ChangeMessage('%s','%s','%s')", $folderid, $id, get_class($message))); - // TODO this could throw several StatusExceptions like e.g. SYNC_STATUS_OBJECTNOTFOUND, SYNC_STATUS_SYNCCANNOTBECOMPLETED - - if (isset($message->flag)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->ChangeMessage('Setting flag')")); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - $this->imap_reopen_folder($folderImapid); - - if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { - if (isset($message->flag->flagstatus) && $message->flag->flagstatus == 2) { - ZLog::Write(LOGLEVEL_DEBUG, "Set On FollowUp -> IMAP Flagged"); - $status = @imap_setflag_full($this->mbox, $id, "\\Flagged", ST_UID); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "Clearing Flagged"); - $status = @imap_clearflag_full($this->mbox, $id, "\\Flagged", ST_UID); - } - - if ($status) { - ZLog::Write(LOGLEVEL_DEBUG, "Flagged changed"); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "Flagged failed"); - } - } - else { - throw new StatusException(sprintf("BackendIMAP->ChangeMessage(): Message is outside the sync range"), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - } - } - - return $this->StatMessage($folderid, $id); - } - - /** - * Changes the 'read' flag of a message on disk - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param int $flags read flag of the message - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function SetReadFlag($folderid, $id, $flags, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SetReadFlag('%s','%s','%s')", $folderid, $id, $flags)); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - $this->imap_reopen_folder($folderImapid); - - if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { - if ($flags == 0) { - // set as "Unseen" (unread) - $status = @imap_clearflag_full($this->mbox, $id, "\\Seen", ST_UID); - } else { - // set as "Seen" (read) - $status = @imap_setflag_full($this->mbox, $id, "\\Seen", ST_UID); - } - } - else { - throw new StatusException(sprintf("BackendIMAP->SetReadFlag(): Message is outside the sync range"), SYNC_STATUS_OBJECTNOTFOUND); - } - - return $status; - } - - /** - * Called when the user has requested to delete (really delete) a message - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function DeleteMessage($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->DeleteMessage('%s','%s')", $folderid, $id)); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - $this->imap_reopen_folder($folderImapid); - - if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { - $s1 = @imap_delete ($this->mbox, $id, FT_UID); - $s11 = @imap_setflag_full($this->mbox, $id, "\\Deleted", FT_UID); - $s2 = @imap_expunge($this->mbox); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->DeleteMessage('%s','%s'): result: s-delete: '%s' s-expunge: '%s' setflag: '%s'", $folderid, $id, $s1, $s2, $s11)); - } - else { - throw new StatusException(sprintf("BackendIMAP->DeleteMessage(): Message is outside the sync range"), SYNC_STATUS_OBJECTNOTFOUND); - } - - return ($s1 && $s2 && $s11); - } - - /** - * Called when the user moves an item on the PDA from one folder to another - * - * @param string $folderid id of the source folder - * @param string $id id of the message - * @param string $newfolderid id of the destination folder - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions - */ - public function MoveMessage($folderid, $id, $newfolderid, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s')", $folderid, $id, $newfolderid)); - $folderImapid = $this->getImapIdFromFolderId($folderid); - $newfolderImapid = $this->getImapIdFromFolderId($newfolderid); - - if ($folderImapid == $newfolderImapid) { - throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, destination folder is source folder. Canceling the move.", $folderid, $id, $newfolderid), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); - } - - $this->imap_reopen_folder($folderImapid); - - if ($this->imap_inside_cutoffdate(Utils::GetCutOffDate($contentparameters->GetFilterType()), $id)) { - // read message flags - $overview = @imap_fetch_overview($this->mbox, $id, FT_UID); - - if (!is_array($overview)) { - throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to retrieve overview of source message: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - } - else { - // get next UID for destination folder - // when moving a message we have to announce through ActiveSync the new messageID in the - // destination folder. This is a "guessing" mechanism as IMAP does not inform that value. - // when lots of simultaneous operations happen in the destination folder this could fail. - // in the worst case the moved message is displayed twice on the mobile. - $destStatus = imap_status($this->mbox, $this->server . $newfolderImapid, SA_ALL); - if (!$destStatus) - throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, unable to open destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); - - $newid = $destStatus->uidnext; - - // move message - $s1 = imap_mail_move($this->mbox, $id, $newfolderImapid, CP_UID); - if (!$s1) - throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, copy to destination folder failed: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - - // delete message in from-folder - $s2 = imap_expunge($this->mbox); - - // open new folder - $stat = $this->imap_reopen_folder($newfolderImapid); - if (!$stat) - throw new StatusException(sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): Error, opening the destination folder: %s", $folderid, $id, $newfolderid, imap_last_error()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - - // remove all flags - $s3 = @imap_clearflag_full($this->mbox, $newid, "\\Seen \\Answered \\Flagged \\Deleted \\Draft", FT_UID); - $newflags = ""; - $move_to_trash = strcasecmp($newfolderImapid, $this->create_name_folder(IMAP_FOLDER_TRASH)) == 0; - - if ($overview[0]->seen || ($move_to_trash && defined('IMAP_AUTOSEEN_ON_DELETE') && IMAP_AUTOSEEN_ON_DELETE == true)) - $newflags .= "\\Seen"; - if ($overview[0]->flagged) - $newflags .= " \\Flagged"; - if ($overview[0]->answered) - $newflags .= " \\Answered"; - $s4 = @imap_setflag_full ($this->mbox, $newid, $newflags, FT_UID); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MoveMessage('%s','%s','%s'): result s-move: '%s' s-expunge: '%s' unset-Flags: '%s' set-Flags: '%s'", $folderid, $id, $newfolderid, Utils::PrintAsString($s1), Utils::PrintAsString($s2), Utils::PrintAsString($s3), Utils::PrintAsString($s4))); - - // return the new id "as string" - return $newid . ""; - } - } - else { - throw new StatusException(sprintf("BackendIMAP->MoveMessage(): Message is outside the sync range"), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - } - } - - - /** - * Processes a response to a meeting request. - * - * @param string $requestid id of the object containing the request - * @param string $folderid id of the parent folder of $requestid - * @param string $response - * - * @access public - * @return string id of the created/updated calendar obj - * @throws StatusException - */ - public function MeetingResponse($requestid, $folderid, $response) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MeetingResponse('%s','%s','%s')", $requestid, $folderid, $response)); - - $folderImapid = $this->getImapIdFromFolderId($folderid); - $this->imap_reopen_folder($folderImapid); - $mail = @imap_fetchheader($this->mbox, $requestid, FT_UID) . @imap_body($this->mbox, $requestid, FT_PEEK | FT_UID); - - if (empty($mail)) { - throw new StatusException("BackendIMAP->MeetingResponse(): Error, message not found, maybe was moved", SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - // Get the original calendar request, so we don't need to create it from scratch - $mobj = new Mail_mimeDecode($mail); - unset($mail); - $message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); - unset($mobj); - - $body_part = null; - if(isset($message->parts)) { - $mparts = $message->parts; - for ($i=0; $i < count($mparts); $i++) { - $part = $mparts[$i]; - //recursively add parts - if ((isset($part->ctype_primary) && $part->ctype_primary == "multipart") - && (isset($part->ctype_secondary) && ($part->ctype_secondary == "mixed" || $part->ctype_secondary == "alternative" || $part->ctype_secondary == "related"))) { - foreach($part->parts as $spart) - $mparts[] = $spart; - continue; - } - - if (is_calendar($part)) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->MeetingResponse - text/calendar part found, trying to reply"); - // FIXME: here we should use the user email address, that could not be username - $body_part = reply_meeting_calendar($part, $response, $this->username); - } - } - unset($mparts); - } - unset($message); - - if ($body_part === null) { - throw new StatusException("BackendIMAP->MeetingResponse(): Error, no calendar part modified", SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - } - - $uuid_calendar = ""; - switch($response) { - case 1: // ACCEPTED - case 2: // TENTATIVE - $uuid_calendar = create_calendar_dav($body_part); - break; - case 3: // DECLINED - // Do nothing - break; - } - - // We don't need to send a reply, because the client will do it - - // Remove message: answered invitation - // Roundcube client doesn't remove the original message, but Zarafa backend does - $s1 = @imap_delete ($this->mbox, $requestid, FT_UID); - $s11 = @imap_setflag_full($this->mbox, $requestid, "\\Deleted", FT_UID); - $s2 = @imap_expunge($this->mbox); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->MeetingResponse('%s','%s'): removing message result: s-delete: '%s' s-expunge: '%s' setflag: '%s'", $folderid, $requestid, $s1, $s2, $s11)); - - return $uuid_calendar; - } - - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - // TODO: - return false; - } - - - /** - * Returns the email address and the display name of the user. Used by autodiscover. - * - * @param string $username The username - * - * @access public - * @return Array - */ - public function GetUserDetails($username) { - // If the username it's not the email address, here we will have an error. We try creating a valid address - $email = $username; - if (strpos($username, "@") === false && strlen($this->domain) > 0) { - $email .= "@" . $this->domain; - } - return array('emailaddress' => $email, 'fullname' => getDefaultFullNameValue($username, $this->domain)); - } - - - /** - * Applies settings to and gets informations from the device - * - * @param SyncObject $settings (SyncOOF or SyncUserInformation possible) - * - * @access public - * @return SyncObject $settings - */ - public function Settings($settings) { - if ($settings instanceof SyncOOF) { - $this->settingsOOF($settings); - } - else if ($settings instanceof SyncUserInformation) { - $this->settingsUserInformation($settings); - } - - return $settings; - } - - - /** - * Indicates which AS version is supported by the backend. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_14; - } - - - /** - * Returns the BackendIMAP as it implements the ISearchProvider interface - * This could be overwritten by the global configuration - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider() { - return $this; - } - - - /**---------------------------------------------------------------------------------------------------------- - * public ISearchProvider methods - */ - - /** - * Indicates if a search type is supported by this SearchProvider - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - return ($searchtype == ISearchProvider::SEARCH_MAILBOX); - } - - - /** - * Queries the IMAP backend - * - * @param string $searchquery string to be searched for - * @param string $searchrange specified searchrange - * - * @access public - * @return array search results - */ - public function GetGALSearchResults($searchquery, $searchrange) { - return false; - } - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * @param string $prefix If used with the combined backend here will come the backend id and delimiter - * - * @return array - */ - public function GetMailboxSearchResults($cpo, $prefix = '') { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults()")); - - $items = false; - $searchFolderId = $cpo->GetSearchFolderid(); - $searchRange = explode('-', $cpo->GetSearchRange()); - $filter = $this->getSearchRestriction($cpo); - - // Open the folder to search - $search = true; - - if (empty($searchFolderId)) { - $searchFolderId = $this->getFolderIdFromImapId($this->create_name_folder(IMAP_FOLDER_INBOX), false); - } - - // Convert searchFolderId to IMAP id - $imapId = $this->getImapIdFromFolderId($searchFolderId); - - $listMessages = array(); - $numMessages = 0; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: Filter <%s>", $filter)); - - if ($cpo->GetSearchDeepTraversal()) { // Recursive search - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: Recursive search %s", $imapId)); - $listFolders = @imap_list($this->mbox, $this->server, "*"); - if ($listFolders === false) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->GetMailboxSearchResults: Error recursive list %s", imap_last_error())); - } - else { - foreach ($listFolders as $subFolder) { - if (@imap_reopen($this->mbox, $subFolder)) { - $imapSubFolder = str_replace($this->server, "", $subFolder); - $subFolderId = $this->getFolderIdFromImapId($imapSubFolder); - if ($subFolderId !== false) { // only search found folders - $subList = @imap_search($this->mbox, $filter, SE_UID, "UTF-8"); - if ($subList !== false) { - $numMessages += count($subList); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: SubSearch in %s : %s ocurrences", $imapSubFolder, count($subList))); - $listMessages[] = array($subFolderId => $subList); - } - } - } - } - } - } - else { // Search in folder - if (@imap_reopen($this->mbox, $this->server . $imapId)) { - $subList = @imap_search($this->mbox, $filter, SE_UID, "UTF-8"); - if ($subList !== false) { - $numMessages += count($subList); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: Search in %s : %s ocurrences", $imapId, count($subList))); - $listMessages[] = array($searchFolderId => $subList); - } - } - } - - - if ($numMessages > 0) { - // range for the search results - $rangestart = 0; - $rangeend = SEARCH_MAXRESULTS - 1; - - if (is_array($searchRange) && isset($searchRange[0]) && isset($searchRange[1])) { - $rangestart = $searchRange[0]; - $rangeend = $searchRange[1]; - } - - $querycnt = $numMessages; - $items = array(); - $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt + 1; - $items['range'] = $rangestart.'-'.($querylimit - 1); - $items['searchtotal'] = $querycnt; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: %s entries found, returning %s", $items['searchtotal'], $items['range'])); - - $p = 0; - $pc = 0; - for ($i = $rangestart, $j = 0; $i <= $rangeend && $i < $querycnt; $i++, $j++) { - $keys = array_keys($listMessages[$p]); - $cntFolder = count($listMessages[$p][$keys[0]]); - if ($pc >= $cntFolder) { - $p++; - $pc = 0; - $keys = array_keys($listMessages[$p]); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: %s %s %s %s", $p, $pc, $keys[0], $listMessages[$p][$keys[0]][$pc])); - $foundFolderId = $keys[0]; - $items[$j]['class'] = 'Email'; - $items[$j]['longid'] = $prefix . $foundFolderId . ":" . $listMessages[$p][$foundFolderId][$pc]; - $items[$j]['folderid'] = $prefix . $foundFolderId; - $pc++; - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->GetMailboxSearchResults: No messages found!")); - } - - return $items; - } - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - return true; - } - - /** - * Disconnects from IMAP - * - * @access public - * @return boolean - */ - public function Disconnect() { - // Don't close the mailbox, we will need it open in the Backend methods - return true; - } - - - /** - * Creates a search restriction - * - * @param ContentParameter $cpo - * @return string - */ - private function getSearchRestriction($cpo) { - $searchText = $cpo->GetSearchFreeText(); - $searchGreater = strftime("%Y-%m-%d", strtotime($cpo->GetSearchValueGreater())); - $searchLess = strftime("%Y-%m-%d", strtotime($cpo->GetSearchValueLess())); - - $filter = ''; - if ($searchGreater != '') { - $filter .= ' SINCE "' . $searchGreater . '"'; - } else { - // Only search in sync messages - $limitdate = new DateTime(); - switch (SYNC_FILTERTIME_MAX) { - case SYNC_FILTERTYPE_1DAY: - $limitdate = $limitdate->sub(new DateInterval("P1D")); - break; - case SYNC_FILTERTYPE_3DAYS: - $limitdate = $limitdate->sub(new DateInterval("P3D")); - break; - case SYNC_FILTERTYPE_1WEEK: - $limitdate = $limitdate->sub(new DateInterval("P1W")); - break; - case SYNC_FILTERTYPE_2WEEKS: - $limitdate = $limitdate->sub(new DateInterval("P2W")); - break; - case SYNC_FILTERTYPE_1MONTH: - $limitdate = $limitdate->sub(new DateInterval("P1M")); - break; - case SYNC_FILTERTYPE_3MONTHS: - $limitdate = $limitdate->sub(new DateInterval("P3M")); - break; - case SYNC_FILTERTYPE_6MONTHS: - $limitdate = $limitdate->sub(new DateInterval("P6M")); - break; - default: - $limitdate = false; - break; - } - - if ($limitdate !== false) { - // date format : 7 Jan 2012 - $filter .= ' SINCE "' . ($limitdate->format("d M Y")) . '"'; - } - } - if ($searchLess != '') { - $filter .= ' BEFORE "' . $searchLess . '"'; - } - - $filter .= ' TEXT "' . $searchText . '"'; - - return $filter; - } - - - /**---------------------------------------------------------------------------------------------------------- - * protected IMAP methods - */ - - /** - * Unmasks a hex folderid and returns the imap folder id - * - * @param string $folderid hex folderid generated by convertImapId() - * - * @access protected - * @return string imap folder id - */ - protected function getImapIdFromFolderId($folderid) { - $this->InitializePermanentStorage(); - - if (isset($this->permanentStorage->fmFidFimap)) { - if (isset($this->permanentStorage->fmFidFimap[$folderid])) { - $imapId = $this->permanentStorage->fmFidFimap[$folderid]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getImapIdFromFolderId('%s') = %s", $folderid, $imapId)); - return $imapId; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getImapIdFromFolderId('%s') = %s", $folderid, 'not found')); - return false; - } - } - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getImapIdFromFolderId('%s') = %s", $folderid, 'not initialized!')); - return false; - } - - /** - * Retrieves a hex folderid previousily masked imap - * - * @param string $imapid Imap folder id - * - * @access protected - * @return string hex folder id - */ - protected function getFolderIdFromImapId($imapid, $case_sensitive = true) { - $this->InitializePermanentStorage(); - - if (!isset($this->permanentStorage->fmFimapFid)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFolderIdFromImapId('%s') IMAP cache folder not found, creating one", $imapid)); - // folderId to folderImap mapping - $this->permanentStorage->fmFidFimap = array(); - // folderImap to folderId mapping - $this->permanentStorage->fmFimapFid = array(); - // folderImap to folderId mapping - lowercase - $this->permanentStorage->fmFimapFidLowercase = array(); - - $this->GetFolderList(); - } - - if ($case_sensitive) { - if (isset($this->permanentStorage->fmFimapFid[$imapid])) { - $folderid = $this->permanentStorage->fmFimapFid[$imapid]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFolderIdFromImapId('%s') = %s", $imapid, $folderid)); - return $folderid; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFolderIdFromImapId('%s') = %s", $imapid, 'not found')); - return false; - } - } - else { - if (isset($this->permanentStorage->fmFimapFidLowercase[strtolower($imapid)])) { - $folderid = $this->permanentStorage->fmFimapFidLowercase[strtolower($imapid)]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFolderIdFromImapId('%s', false) = %s", $imapid, $folderid)); - return $folderid; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFolderIdFromImapId('%s', false) = %s", $imapid, 'not found')); - return false; - } - } - - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getFolderIdFromImapId('%s') = %s", $imapid, 'not initialized!')); - return false; - } - - /** - * Masks a imap folder id into a generated hex folderid - * The method getFolderIdFromImapId() is consulted so that an - * imapid always returns the same hex folder id - * - * @param string $imapid Imap folder id - * - * @access protected - * @return string hex folder id - */ - protected function convertImapId($imapid) { - $this->InitializePermanentStorage(); - - // check if this imap id was converted before - $folderid = $this->getFolderIdFromImapId($imapid); - - // nothing found, so generate a new id and put it in the cache - if (!$folderid) { - // generate folderid and add it to the mapping - $folderid = sprintf('%04x%04x', mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )); - -// // folderId to folderImap mapping -// if (!isset($this->permanentStorage->fmFidFimap)) -// $this->permanentStorage->fmFidFimap = array(); - - $a = $this->permanentStorage->fmFidFimap; - $a[$folderid] = $imapid; - $this->permanentStorage->fmFidFimap = $a; - -// // folderImap to folderid mapping -// if (!isset($this->permanentStorage->fmFimapFid)) -// $this->permanentStorage->fmFimapFid = array(); - - $b = $this->permanentStorage->fmFimapFid; - $b[$imapid] = $folderid; - $this->permanentStorage->fmFimapFid = $b; - -// if (!isset($this->permanentStorage->fmFimapFidLowercase)) -// $this->permanentStorage->fmFimapFidLowercase = array(); - - $c = $this->permanentStorage->fmFimapFidLowercase; - $c[strtolower($imapid)] = $folderid; - $this->permanentStorage->fmFimapFidLowercase = $c; - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->convertImapId('%s') = %s", $imapid, $folderid)); - - return $folderid; - } - - /** - * Returns the serverdelimiter for folder parsing - * - * @access protected - * @return string delimiter - */ - protected function getServerDelimiter() { - $this->InitializePermanentStorage(); - if (isset($this->permanentStorage->serverdelimiter)) { - return $this->permanentStorage->serverdelimiter; - } - - $list = @imap_getmailboxes($this->mbox, $this->server, "*"); - if (is_array($list) && count($list) > 0) { - // get the delimiter from the first folder - $delimiter = $list[0]->delimiter; - $this->permanentStorage->serverdelimiter = $delimiter; - } else { - // default - $delimiter = "."; - } - return $delimiter; - } - - /** - * Helper to re-initialize the folder to speed things up - * Remember what folder is currently open and only change if necessary - * - * @param string $folderid id of the folder - * @param boolean $force re-open the folder even if currently opened - * - * @access protected - * @return boolean if folder is opened - */ - protected function imap_reopen_folder($folderid, $force = false) { - // Reconnect - $this->imap_reconnect(); - - // to see changes, the folder has to be reopened! - if ($this->mboxFolder != $folderid || $force) { - $s = @imap_reopen($this->mbox, $this->server . $folderid); - // TODO throw status exception - if (!$s) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->imap_reopen_folder('%s'): failed to change folder: %s", $folderid, implode(", ", imap_errors()))); - return false; - } - $this->mboxFolder = $folderid; - } - - return true; - } - - /** - * Reconnect IMAP connection if needed - * - * @access private - */ - private function imap_reconnect() { - if ($this->mbox) { - imap_ping($this->mbox); - } - else { - $this->mbox = @imap_open($this->server, $this->username, $this->password, OP_HALFOPEN); - $this->mboxFolder = ""; - } - } - - /** - * Creates a new IMAP folder. - * - * @param string $foldername full folder name - * - * @access private - * @return boolean success - */ - private function imap_create_folder($foldername) { - $name = Utils::Utf7_iconv_encode(Utils::Utf8_to_utf7($foldername)); - - $res = @imap_createmailbox($this->mbox, $name); - if ($res) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_create_folder('%s'): new folder created", $foldername)); - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->imap_create_folder('%s'): failed to create folder: %s", $foldername, implode(", ", imap_errors()))); - } - - return $res; - } - - /** - * Check if the message was sent before the cutoffdate. - * - * @access private - * @param integer $cutoffdate EPOCH of the bottom sync range. 0 if no range is defined - * @param integer $id Message id - * @return boolean - */ - private function imap_inside_cutoffdate($cutoffdate, $id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_inside_cutoffdate(): Checking if the messages is withing the cutoffdate %d, %s", $cutoffdate, $id)); - $is_inside = false; - - if ($cutoffdate == 0) { - // No cutoffdate, all the messages are in range - $is_inside = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_inside_cutoffdate(): No cutoffdate, all the messages are in range")); - } - else { - $overview = imap_fetch_overview($this->mbox, $id, FT_UID); - if (is_array($overview)) { - if (isset($overview[0]->date)) { - $epoch_sent = strtotime($overview[0]->date); - $is_inside = ($cutoffdate <= $epoch_sent); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_inside_cutoffdate(): Message is %s cutoffdate range", ($is_inside ? "INSIDE" : "OUTSIDE"))); - } - else { - // No sent date defined, that's a buggy message but we will think that the message is in range - $is_inside = true; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_inside_cutoffdate(): No sent date defined, that's a buggy message but we will think that the message is in range")); - } - } - else { - // No overview, maybe the message is no longer there - $is_inside = false; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->imap_inside_cutoffdate(): No overview, maybe the message is no longer there")); - } - } - - return $is_inside; - } - - /** - * Adds a message with seen flag to a specified folder (used for saving sent items) - * - * @param string $folderid id of the folder - * @param string $header header of the message - * @param long $body body of the message - * - * @access protected - * @return boolean status - */ - protected function addSentMessage($folderid, $header, $body) { - $header_body = str_replace("\n", "\r\n", str_replace("\r", "", $header . "\n\n" . $body)); - - return @imap_append($this->mbox, $this->server . $folderid, $header_body, "\\Seen"); - } - - /** - * Parses an mimedecode address array back to a simple "," separated string - * - * @param array $ad addresses array - * - * @access protected - * @return string mail address(es) string - */ - protected function parseAddr($ad) { - $addr_string = ""; - if (isset($ad) && is_array($ad)) { - foreach($ad as $addr) { - if ($addr_string) $addr_string .= ","; - $addr_string .= $addr->mailbox . "@" . $addr->host; - } - } - return $addr_string; - } - - /** - * Recursive way to get mod and parent - repeat until only one part is left - * or the folder is identified as an IMAP folder - * - * @param string $fhir folder hierarchy string - * @param string &$displayname reference of the displayname - * @param long &$parent reference of the parent folder - * - * @access protected - * @return - */ - protected function getModAndParentNames($fhir, &$displayname, &$parent) { - // if mod is already set add the previous part to it as it might be a folder which has delimiter in its name - $displayname = (isset($displayname) && strlen($displayname) > 0) ? $displayname = array_pop($fhir) . $this->getServerDelimiter() . $displayname : array_pop($fhir); - $parent = implode($this->getServerDelimiter(), $fhir); - - if (count($fhir) == 1 || $this->checkIfIMAPFolder($parent)) { - return; - } - //recursion magic - $this->getModAndParentNames($fhir, $displayname, $parent); - } - - /** - * Prepare the folder name to get the type and parent. - * - * @param string $folder_name - * @return string - * @access private - */ - private function create_name_folder($folder_name) { - $foldername = $folder_name; - // If we have defined a folder prefix, and it's not empty - if (defined('IMAP_FOLDER_PREFIX') && IMAP_FOLDER_PREFIX != "") { - // If inbox uses prefix or we are not evaluating inbox - if (IMAP_FOLDER_PREFIX_IN_INBOX == true || strcasecmp($foldername, IMAP_FOLDER_INBOX) != 0) { - $foldername = IMAP_FOLDER_PREFIX . $this->getServerDelimiter() . $foldername; - } - } - - return $foldername; - } - - /** - * Checks if a specified name is a folder in the IMAP store - * - * @param string $foldername a foldername - * - * @access protected - * @return boolean - */ - protected function checkIfIMAPFolder($folderName) { - $folder_name = $folderName; - if (defined('IMAP_FOLDER_PREFIX') && strlen(IMAP_FOLDER_PREFIX) > 0) { - // TODO: We don't care about the inbox exception with the prefix, because we won't check inbox - $folder_name = IMAP_FOLDER_PREFIX . $this->getServerDelimiter() . $folder_name; - } - $list_subfolders = @imap_list($this->mbox, $this->server, $folder_name); - return is_array($list_subfolders); - } - - /** - * Removes parenthesis (comments) from the date string because - * strtotime returns false if received date has them - * - * @param string $receiveddate a date as a string - * - * @access protected - * @return integer - */ - protected function cleanupDate($receiveddate) { - if (is_array($receiveddate)) { - // Header Date could be repeated in the message, we only check the first - $receiveddate = $receiveddate[0]; - } - $receivedtime = strtotime(preg_replace('/\(.*\)/', "", $receiveddate)); - if ($receivedtime === false || $receivedtime == -1) { - ZLog::Write(LOGLEVEL_WARN, sprintf("cleanupDate('%s'): strtotime() failed - message might be broken.", $receiveddate)); - return null; - } - - return $receivedtime; - } - - - /** - * Returns a list of mime-types with extension files - * - * @access private - * @return array[mime-type => extension] - */ - private function SystemExtensionMimeTypes() { - $out = array(); - if (file_exists(SYSTEM_MIME_TYPES_MAPPING)) { - $file = fopen(SYSTEM_MIME_TYPES_MAPPING, 'r'); - while(($line = fgets($file)) !== false) { - $line = trim(preg_replace('/#.*/', '', $line)); - if(!$line) - continue; - $parts = preg_split('/\s+/', $line); - if(count($parts) == 1) - continue; - $type = array_shift($parts); - foreach($parts as $part) { - if (!isset($out[$type])) { - $out[$type] = $part; - } - } - } - fclose($file); - } - - return $out; - } - - - /** - * Sends a message - * - * @access private - * @param $fromaddr From address - * @param $toaddr To address - * @param $headers Headers array - * @param $body Body array - * @return boolean True if sent - * @throws StatusException - */ - private function sendMessage($fromaddr, $toaddr, $headers, $body) { - global $imap_smtp_params; - - $sendingMethod = 'mail'; - if (defined('IMAP_SMTP_METHOD')) { - $sendingMethod = IMAP_SMTP_METHOD; - if ($sendingMethod == 'smtp') { - if (isset($imap_smtp_params['username']) && $imap_smtp_params['username'] == 'imap_username') { - $imap_smtp_params['username'] = $this->username; - } - if (isset($imap_smtp_params['password']) && $imap_smtp_params['password'] == 'imap_password') { - $imap_smtp_params['password'] = $this->password; - } - } - } - - if (is_array($toaddr)) { - $recipients = $toaddr; - } - else { - $recipients = array($toaddr); - } - - // Cc and Bcc headers are sent, but we need to make sure that the recipient list contains them - foreach (array("CC", "cc", "Cc", "BCC", "Bcc", "bcc") as $key) { - if (!empty($headers[$key])) { - if (is_array($headers[$key])) { - $recipients = array_merge($recipients, $headers[$key]); - } - else { - $recipients[] = $headers[$key]; - } - } - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->sendMessage(): SendingMail with %s", $sendingMethod)); - $mail =& Mail::factory($sendingMethod, $sendingMethod == "mail" ? "-f " . $fromaddr : $imap_smtp_params); - $send = $mail->send($recipients, $headers, $body); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->sendMessage(): send return value %s", $send)); - - if ($send !== true) { - throw new StatusException(sprintf("BackendIMAP->sendMessage(): The email could not be sent"), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); - } - - return $send; - } - - - /** - * Saves a copy of a message in the Sent folder - * - * @access public - * @param $finalHeaders Array of headers - * @param $finalBody Body part - * @return boolean If the message is saved - */ - private function saveSentMessage($finalHeaders, $finalBody) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->saveSentMessage(): saving message in Sent Items folder")); - - $headers = ""; - foreach ($finalHeaders as $k => $v) { - if (strlen($headers) > 0) { - $headers .= "\n"; - } - $headers .= "$k: $v"; - } - - if ($this->sentID === false) { - $this->sentID = $this->getFolderIdFromImapId($this->create_name_folder(IMAP_FOLDER_SENT), false); - } - - $saved = false; - if ($this->sentID) { - $imapid = $this->getImapIdFromFolderId($this->sentID); - $saved = $this->addSentMessage($imapid, $headers, $finalBody); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->saveSentMessage(): Outgoing mail saved in 'Sent' folder '%s' ['%s']", $imapid, $this->sentID)); - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->saveSentMessage(): The email could not be saved to Sent Items folder. Check your configuration."); - } - unset($headers); - - return $saved; - } - - - /** - * Set the from header value if not set or we are overwriting by configuration. - * - * @param array &$headers - * @return void - * @access private - */ - private function setFromHeaderValue(&$headers) { - $from = getDefaultFromValue($this->username, $this->domain); - - if (isset($headers["from"]) && strlen($headers["from"]) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFromHeaderValue(): from defined: %s", $headers["from"])); - if (strlen(IMAP_DEFAULTFROM) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFromHeaderValue(): Overwriting From: %s", $from)); - $headers["from"] = $from; - } - } - elseif (isset($headers["From"]) && strlen($headers["From"]) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFromHeaderValue(): From defined: %s", $headers["From"])); - if (strlen(IMAP_DEFAULTFROM) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFromHeaderValue(): Overwriting From: %s", $from)); - $headers["From"] = $from; - } - } - else { - // not From header found - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getFromHeaderValue(): No From address defined, we try for a default one")); - $headers["from"] = $from; - } - } - - - /** - * Set the Return-Path header value if not set - * - * @param array &$headers - * @param string $fromaddr - * @return void - * @access private - */ - private function setReturnPathValue(&$headers, $fromaddr) { - if (!(isset($headers["return-path"]) || isset($headers["Return-Path"]))) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->setReturnPathValue(): No Return-Path address defined, we use From")); - $headers["return-path"] = $fromaddr; - } - } - - - /** - * The meta function for out of office settings. - * - * @param SyncObject $oof - * - * @access private - * @return void - */ - private function settingsOOF(&$oof) { - //if oof state is set it must be set of oof and get otherwise - if (!isset($oof->oofstate)) { - $oof->oofstate = SYNC_SETTINGSOOF_DISABLED; - $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS; - - //unset body type for oof in order not to stream it - unset($oof->bodytype); - return true; - } - else { - return false; - } - } - - - /** - * Gets the user's email address from server - * - * @param SyncObject $userinformation - * - * @access private - * @return void - */ - private function settingsUserInformation(&$userinformation) { - $userinformation->Status = SYNC_SETTINGSSTATUS_USERINFO_SUCCESS; - $userinformation->emailaddresses[] = $this->username; - return true; - } - - - /** - * Gets the folder list - * - * @access private - * @return array - */ - private function get_folder_list() { - $folders = array(); - $list = @imap_getmailboxes($this->mbox, $this->server, "*"); - if (is_array($list)) { - $list = array_reverse($list); - foreach ($list as $l) { - $folders[] = $l->name; - } - } - - return $folders; - } - - /** - * Add one address to the list - * - * @access private - * @param array $addresses - * @param RFC822 address object $addr - * @return string - */ - private function add_address_to_list(&$addresses, $addr) { - $name = ""; - - if (isset($addr->mailbox) && isset($addr->host) && isset($addr->personal)) { - $address = sprintf("%s@%s", $addr->mailbox, $addr->host); - $name = $addr->personal; - - if(strlen($name) == 0 || $name == $address) { - $fulladdr = $address; - } - else { - if (preg_match('/^\".*\"$/', $name)) { - $fulladdr = sprintf("%s <%s>", $name, $address); - } - else { - $fulladdr = sprintf("\"%s\" <%s>", $name, $address); - } - } - - array_push($addresses, $fulladdr); - } - - return $name; - } - - /** - * Close the IMAP connection. - * - * @access private - */ - private function close_connection() { - if ($this->mbox) { - // list all errors - $errors = imap_errors(); - if (is_array($errors)) { - foreach ($errors as $e) { - if (stripos($e, "fail") !== false) { - $level = LOGLEVEL_WARN; - } - else { - $level = LOGLEVEL_DEBUG; - } - ZLog::Write($level, "BackendIMAP->close_connection(): IMAP said: " . $e); - } - } - @imap_close($this->mbox); - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->close_connection(): disconnected from IMAP server"); - $this->mbox = false; - } - } -}; diff --git a/sources/backend/imap/mime_calendar.php b/sources/backend/imap/mime_calendar.php deleted file mode 100644 index 9592ba8..0000000 --- a/sources/backend/imap/mime_calendar.php +++ /dev/null @@ -1,357 +0,0 @@ -create_calendar_dav(): Creating calendar event"); - - if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) { - $caldav = new BackendCalDAV(); - if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) { - $etag = $caldav->CreateUpdateCalendar($data); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->create_calendar_dav(): Calendar created with etag '%s' and data <%s>", $etag, $data)); - $caldav->Logoff(); - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->create_calendar_dav(): Error connecting with BackendCalDAV"); - } - } -} - -function delete_calendar_dav($uid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->delete_calendar_dav('%s'): Deleting calendar event", $uid)); - - if ($uid === false) { - ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->delete_calendar_dav(): UID not found; report the full calendar object to developers"); - } - else { - if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) { - $caldav = new BackendCalDAV(); - if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) { - $events = $caldav->FindCalendar($uid); - if (count($events) == 1) { - $href = $events[0]["href"]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->delete_calendar_dav(): found event with href '%s', deleting", $href)); - // Delete event - $res = $caldav->DeleteCalendar($href); - if ($res) { - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->delete_calendar_dav(): event deleted"); - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): error removing event, we will end with zombie events"); - } - $caldav->Logoff(); - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): event not found, we will end with zombie events"); - } - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->delete_calendar_dav(): Error connecting with BackendCalDAV"); - } - } - } -} - - -function update_calendar_attendee($uid, $mailto, $status) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee('%s', '%s', '%s'): Updating calendar event attendee", $uid, $mailto, $status)); - $updated = false; - - if ($uid === false) { - ZLog::Write(LOGLEVEL_WARN, "BackendIMAP->update_calendar_attendee(): UID not found; report the full calendar object to developers"); - } - else { - if (defined('IMAP_MEETING_USE_CALDAV') && IMAP_MEETING_USE_CALDAV) { - $caldav = new BackendCalDAV(); - if ($caldav->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword())) { - $events = $caldav->FindCalendar($uid); - if (count($events) == 1) { - $href = $events[0]["href"]; - $etag = $events[0]["etag"]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): found event with href '%s' etag '%s'; updating", $href, $etag)); - - // Get Attendee status - $old_status = ""; - - if (strcasecmp($old_status, $status) != 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Before <%s>", $events[0]["data"])); - $ical = new iCalComponent(); - $ical->ParseFrom($events[0]["data"]); - $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", strtoupper($status), $mailto); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): After <%s>", $ical->Render())); - $etag = $caldav->CreateUpdateCalendar($ical->Render(), $href, $etag); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->update_calendar_attendee(): Calendar updated with etag '%s'", $etag)); - // Update new status - $updated = true; - } - - $caldav->Logoff(); - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): event not found or duplicated event"); - } - } - else { - ZLog::Write(LOGLEVEL_ERROR, "BackendIMAP->update_calendar_attendee(): Error connecting with BackendCalDAV"); - } - } - } - - return $updated; -} - -/** - * Detect if one message has one VCALENDAR part - * - * @param Mail_mimeDecode $message - * @return boolean - * @access private - */ -function has_calendar_object($message) { - if (is_calendar($message)) { - return true; - } - else { - if(isset($message->parts)) { - for ($i = 0; $i < count($message->parts); $i++) { - if (is_calendar($message->parts[$i])) { - return true; - } - } - } - } - - return false; -} - - -/** - * Detect if the message-part is VCALENDAR - * Content-Type: text/calendar; - * - * @param Mail_mimeDecode $message - * @return boolean - * @access private - */ -function is_calendar($message) { - return isset($message->ctype_primary) && isset($message->ctype_secondary) && $message->ctype_primary == "text" && $message->ctype_secondary == "calendar"; -} - - -/** - * Converts a text/calendar part into SyncMeetingRequest - * This is called on received messages, it's not called for events generated from the mobile - * - * @access private - * @param $part MIME part - * @param $output SyncMail object - * @param $is_sent_folder boolean - */ -function parse_meeting_calendar($part, &$output, $is_sent_folder) { - $ical = new iCalComponent(); - $ical->ParseFrom($part->body); - ZLog::Write(LOGLEVEL_WBXML, sprintf("BackendIMAP->parse_meeting_calendar(): %s", $part->body)); - - // Get UID - $uid = false; - $props = $ical->GetPropertiesByPath("VEVENT/UID"); - if (count($props) > 0) { - $uid = $props[0]->Value(); - } - - $method = false; - $props = $ical->GetPropertiesByPath("VCALENDAR/METHOD"); - if (count($props) > 0) { - $method = strtolower($props[0]->Value()); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar(): Using method from vcalendar object: %s", $method)); - } - else { - if (isset($part->ctype_parameters["method"])) { - $method = strtolower($part->ctype_parameters["method"]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar(): Using method from mime part object: %s", $method)); - } - } - - if ($method === false) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - No method header, please report it to the developers")); - $output->messageclass = "IPM.Appointment"; - } - else { - switch ($method) { - case "cancel": - $output->messageclass = "IPM.Schedule.Meeting.Canceled"; - $output->meetingrequest->disallownewtimeproposal = 1; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Event canceled, removing calendar object"); - delete_calendar_dav($uid); - break; - case "counter": - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Counter received"); - $output->messageclass = "IPM.Schedule.Meeting.Resp.Tent"; - $output->meetingrequest->disallownewtimeproposal = 0; - break; - case "reply": - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Reply received"); - $props = $ical->GetPropertiesByPath('VEVENT/ATTENDEE'); - - for ($i = 0; $i < count($props); $i++) { - $mailto = $props[$i]->Value(); - $props_params = $props[$i]->Parameters(); - $status = strtolower($props_params["PARTSTAT"]); - if (!$is_sent_folder) { - // Only evaluate received replies, not sent - $res = update_calendar_attendee($uid, $mailto, $status); - } - else { - $res = true; - } - if ($res) { - // Only set messageclass for replies changing my calendar object - switch ($status) { - case "accepted": - $output->messageclass = "IPM.Schedule.Meeting.Resp.Pos"; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> accepted"); - break; - case "needs-action": - $output->messageclass = "IPM.Schedule.Meeting.Resp.Tent"; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> needs-action"); - break; - case "tentative": - $output->messageclass = "IPM.Schedule.Meeting.Resp.Tent"; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> tentative"); - break; - case "declined": - $output->messageclass = "IPM.Schedule.Meeting.Resp.Neg"; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): Update attendee -> declined"); - break; - default: - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown reply status <%s>, please report it to the developers", $status)); - $output->messageclass = "IPM.Appointment"; - break; - } - } - } - $output->meetingrequest->disallownewtimeproposal = 1; - break; - case "request": - $output->messageclass = "IPM.Schedule.Meeting.Request"; - $output->meetingrequest->disallownewtimeproposal = 0; - ZLog::Write(LOGLEVEL_DEBUG, "BackendIMAP->parse_meeting_calendar(): New request"); - // New meeting, we don't create it now, because we need to confirm it first, but if we don't create it we won't see it in the calendar - break; - default: - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->parse_meeting_calendar() - Unknown method <%s>, please report it to the developers", strtolower($part->headers["method"]))); - $output->messageclass = "IPM.Appointment"; - $output->meetingrequest->disallownewtimeproposal = 0; - break; - } - } - - $props = $ical->GetPropertiesByPath('VEVENT/DTSTAMP'); - if (count($props) == 1) { - $output->meetingrequest->dtstamp = Utils::MakeUTCDate($props[0]->Value()); - } - $props = $ical->GetPropertiesByPath('VEVENT/UID'); - if (count($props) == 1) { - $output->meetingrequest->globalobjid = $props[0]->Value(); - } - $props = $ical->GetPropertiesByPath('VEVENT/DTSTART'); - if (count($props) == 1) { - $output->meetingrequest->starttime = Utils::MakeUTCDate($props[0]->Value()); - if (strlen($props[0]->Value()) == 8) { - $output->meetingrequest->alldayevent = 1; - } - } - $props = $ical->GetPropertiesByPath('VEVENT/DTEND'); - if (count($props) == 1) { - $output->meetingrequest->endtime = Utils::MakeUTCDate($props[0]->Value()); - if (strlen($props[0]->Value()) == 8) { - $output->meetingrequest->alldayevent = 1; - } - } - $props = $ical->GetPropertiesByPath('VEVENT/ORGANIZER'); - if (count($props) == 1) { - $output->meetingrequest->organizer = str_ireplace("MAILTO:", "", $props[0]->Value()); - } - $props = $ical->GetPropertiesByPath('VEVENT/LOCATION'); - if (count($props) == 1) { - $output->meetingrequest->location = $props[0]->Value(); - } - $props = $ical->GetPropertiesByPath('VEVENT/CLASS'); - if (count($props) == 1) { - switch ($props[0]->Value()) { - case "PUBLIC": - $output->meetingrequest->sensitivity = "0"; - break; - case "PRIVATE": - $output->meetingrequest->sensitivity = "2"; - break; - case "CONFIDENTIAL": - $output->meetingrequest->sensitivity = "3"; - break; - default: - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->parse_meeting_calendar() - No sensitivity class. Using 2")); - $output->meetingrequest->sensitivity = "2"; - break; - } - } - - // Get $tz from first timezone - $props = $ical->GetPropertiesByPath("VTIMEZONE/TZID"); - if (count($props) > 0) { - // TimeZones shouldn't have dots - $tzname = str_replace(".", "", $props[0]->Value()); - $tz = TimezoneUtil::GetFullTZFromTZName($tzname); - } - else { - $tz = TimezoneUtil::GetFullTZ(); - } - $output->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz)); - - // Fixed values - $output->meetingrequest->instancetype = 0; - $output->meetingrequest->responserequested = 1; - $output->meetingrequest->busystatus = 2; - - // TODO: reminder - $output->meetingrequest->reminder = ""; -} - - - -/** - * Modify a text/calendar part to transform it in a reply - * - * @access private - * @param $part MIME part - * @param $response Response numeric value - * @param $condition_value string - * @return string MIME text/calendar - */ -function reply_meeting_calendar($part, $response, $username) { - $status_attendee = "ACCEPTED"; // 1 or default is ACCEPTED - $status_event = "CONFIRMED"; - switch ($response) { - case 1: - $status_attendee = "ACCEPTED"; - $status_event = "CONFIRMED"; - break; - case 2: - $status_attendee = $status_event = "TENTATIVE"; - break; - case 3: - // We won't hit this case ever, because we won't create an event if we are rejecting it - $status_attendee = "DECLINED"; - $status_event = "CANCELLED"; - break; - } - - $ical = new iCalComponent(); - $ical->ParseFrom($part->body); - - $ical->SetPValue("METHOD", "REPLY"); - $ical->SetCPParameterValue("VEVENT", "STATUS", $status_event, null); - // Update my information as attendee, but only mine - $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", $status_attendee, sprintf("MAILTO:%s", $username)); - $ical->SetCPParameterValue("VEVENT", "ATTENDEE", "RSVP", null, sprintf("MAILTO:%s", $username)); - - return $ical->Render(); -} diff --git a/sources/backend/imap/mime_encode.php b/sources/backend/imap/mime_encode.php deleted file mode 100644 index abe2a8a..0000000 --- a/sources/backend/imap/mime_encode.php +++ /dev/null @@ -1,307 +0,0 @@ -disposition) && $part->disposition == "attachment") { - // it's an attachment - $new_part = add_sub_part($email, $part); - } - else { - if (isset($part->ctype_primary) && $part->ctype_primary != "text" && $part->ctype_primary != "multipart") { - // it's not a text part or a multipart - $new_part = add_sub_part($email, $part); - } - } - if (isset($part->parts)) { - // We add sub-parts to the new part (if any), not to the main message. Recursive calling - if ($new_part === null) { - add_extra_sub_parts($email, $part->parts); - } - else { - add_extra_sub_parts($new_part, $part->parts); - } - } - } - } -} - -/** - * Add a subpart to a mimepart object. - * - * @param Mail_mimePart $email reference to the object - * @param object $part message part - * - * @access private - * @return void - */ -function add_sub_part(&$email, $part) { - //http://tools.ietf.org/html/rfc4021 - $new_part = null; - $params = array(); - $params['content_type'] = ''; - if (isset($part) && isset($email)) { - if (isset($part->ctype_primary)) { - $params['content_type'] = $part->ctype_primary; - } - if (isset($part->ctype_secondary)) { - $params['content_type'] .= '/' . $part->ctype_secondary; - } - if (isset($part->ctype_parameters)) { - foreach ($part->ctype_parameters as $k => $v) { - if(strcasecmp($k, 'boundary') != 0) { - $params['content_type'] .= '; ' . $k . '=' . $v; - } - } - } - if (isset($part->disposition)) { - $params['disposition'] = $part->disposition; - } - //FIXME: dfilename => filename - if (isset($part->d_parameters)) { - $params['headers_charset'] = 'utf-8'; - foreach ($part->d_parameters as $k => $v) { - $params[$k] = $v; - } - } - foreach ($part->headers as $k => $v) { - switch($k) { - case "content-description": - $params['description'] = $v; - break; - case "content-type": - case "content-disposition": - case "content-transfer-encoding": - // Do nothing, we already did - break; - case "content-id": - $params['cid'] = str_replace('<', '', str_replace('>', '', $v)); - break; - default: - $params[$k] = $v; - break; - } - } - - // If not exist body, the part will be multipart/alternative, so we don't add encoding - if (!isset($params['encoding']) && isset($part->body)) { - $params['encoding'] = 'base64'; - } - // We could not have body; recursive messages - $new_part = $email->addSubPart(isset($part->body) ? $part->body : "", $params); - unset($params); - } - - // return the new part - return $new_part; -} - -/** - * Add a subpart to a mimepart object. - * - * @param Mail_mimePart $email reference to the object - * @param object $part message part - * - * @access private - * @return void - */ -function change_charset_and_add_subparts(&$email, $part) { - if (isset($part)) { - $new_part = null; - if (isset($part->ctype_parameters['charset'])) { - $part->ctype_parameters['charset'] = 'UTF-8'; - $new_part = add_sub_part($email, $part); - } - else { - // We don't add the charset because it could be a non-text part - $new_part = add_sub_part($email, $part); - } - - if (isset($part->parts)) { - foreach ($part->parts as $subpart) { - // Subparts are added to the part, not the main message - change_charset_and_add_subparts($new_part, $subpart); - } - } - } -} - -/** - * Creates a MIME message from a decoded MIME message, reencoding and fixing the text. - * - * @param array $message array returned from Mail_mimeDecode->decode - * - * @access public - * @return string MIME message - */ -function build_mime_message($message) { - $finalEmail = new Mail_mimePart(isset($message->body) ? $message->body : "", array('headers' => $message->headers)); - if (isset($message->parts)) { - foreach ($message->parts as $part) { - change_charset_and_add_subparts($finalEmail, $part); - } - } - - $mimeHeaders = Array(); - $mimeHeaders['headers'] = Array(); - $is_mime = false; - foreach ($message->headers as $key => $value) { - switch($key) { - case 'content-type': - $new_value = $message->ctype_primary . "/" . $message->ctype_secondary; - $is_mime = (strcasecmp($message->ctype_primary, 'multipart') == 0); - - if (isset($message->ctype_parameters)) { - foreach ($message->ctype_parameters as $ckey => $cvalue) { - switch($ckey) { - case 'charset': - $new_value .= '; charset="UTF-8"'; - break; - case 'boundary': - // Do nothing, we are encoding also the headers - break; - default: - $new_value .= '; ' . $ckey . '="' . $cvalue . '"'; - break; - } - } - } - - $mimeHeaders['content_type'] = $new_value; - break; - case 'content-transfer-encoding': - if (strcasecmp($value, "base64") == 0 || strcasecmp($value, "binary") == 0) { - $mimeHeaders['encoding'] = "base64"; - } - else { - $mimeHeaders['encoding'] = "8bit"; - } - break; - case 'content-id': - $mimeHeaders['cid'] = $value; - break; - case 'content-location': - $mimeHeaders['location'] = $value; - break; - case 'content-disposition': - $mimeHeaders['disposition'] = $value; - break; - case 'content-description': - $mimeHeaders['description'] = $value; - break; - default: - if (is_array($value)) { - foreach($value as $v) { - $mimeHeaders['headers'][$key] = $v; - } - } - else { - $mimeHeaders['headers'][$key] = $value; - } - break; - } - } - - $finalEmail = new Mail_mimePart(isset($message->body) ? $message->body : "", $mimeHeaders); - unset($mimeHeaders['headers']); - unset($mimeHeaders); - - if (isset($message->parts)) { - foreach ($message->parts as $part) { - change_charset_and_add_subparts($finalEmail, $part); - } - } - - $boundary = '=_' . md5(rand() . microtime()); - $finalEmail = $finalEmail->encode($boundary); - - $headers = ""; - $mimePart = new Mail_mimePart(); - foreach ($finalEmail['headers'] as $key => $value) { - if (is_array($value)) { - foreach ($values as $ikey => $ivalue) { - $headers .= $key . ": " . $mimePart->encodeHeader($key, $ivalue, "utf-8", "base64") . "\n"; - } - } - else { - $headers .= $key . ": " . $mimePart->encodeHeader($key, $value, "utf-8", "base64") . "\n"; - } - } - unset($mimePart); - - - if ($is_mime) { - $built_message = "$headers\nThis is a multi-part message in MIME format.\n".$finalEmail['body']; - } - else { - $built_message = "$headers\n".$finalEmail['body']; - } - unset($headers); - unset($finalEmail); - - return $built_message; -} - - -/** - * Detect if the message-part is SMIME - * - * @param Mail_mimeDecode $message - * @return boolean - * @access public - */ -function is_smime($message) { - $res = false; - - if (isset($message->ctype_primary) && isset($message->ctype_secondary)) { - $smime_types = array(array("multipart", "signed"), array("application", "pkcs7-mime"), array("application", "x-pkcs7-mime"), array("multipart", "encrypted")); - for ($i = 0; $i < count($smime_types) && !$res; $i++) { - $res = ($message->ctype_primary == $smime_types[$i][0] && $message->ctype_secondary == $smime_types[$i][1]); - } - } - - return $res; -} - - -/** - * Detect if the message-part is SMIME, encrypted but not signed - * #190, KD 2015-06-04 - * - * @param Mail_mimeDecode $message - * @return boolean - * @access public - */ -function is_encrypted($message) { - $res = false; - - if (is_smime($message) && !($message->ctype_primary == "multipart" && $message->ctype_secondary == "signed")) { - $res = true; - } - - return $res; -} - - -/** - * Detect if the message is multipart. - * #198, KD 2015-06-15 - * - * @param Mail_mimeDecode $message - * @return boolean - * @access public - */ -function is_multipart($message) { - return isset($message->ctype_primary) && $message->ctype_primary == "multipart"; -} \ No newline at end of file diff --git a/sources/backend/imap/user_identity.php b/sources/backend/imap/user_identity.php deleted file mode 100644 index 05c1077..0000000 --- a/sources/backend/imap/user_identity.php +++ /dev/null @@ -1,232 +0,0 @@ -getIdentityFromLdap() - Connected to LDAP")); - ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3); - ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0); - $ldap_bind = ldap_bind($ldap_conn, IMAP_FROM_LDAP_USER, IMAP_FROM_LDAP_PASSWORD); - - if ($ldap_bind) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Authenticated in LDAP")); - $filter = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_LDAP_QUERY)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Searching From with filter: %s", $filter)); - $search = ldap_search($ldap_conn, IMAP_FROM_LDAP_BASE, $filter, unserialize(IMAP_FROM_LDAP_FIELDS)); - $items = ldap_get_entries($ldap_conn, $search); - if ($items['count'] > 0) { - $ret_value = $identity; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Found entry in LDAP. Generating From")); - // We get the first object. It's your responsability to make the query unique - foreach (unserialize(IMAP_FROM_LDAP_FIELDS) as $field) { - $ret_value = str_replace('#'.$field, $items[0][$field][0], $ret_value); - } - if ($encode) { - $ret_value = encodeFrom($ret_value); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - No entry found in LDAP")); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Not authenticated in LDAP server")); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromLdap() - Not connected to LDAP server")); - } - } - catch(Exception $ex) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromLdap() - Error getting From value from LDAP server: %s", $ex)); - } - - if ($ldap_conn != null) { - ldap_close($ldap_conn); - } - - return $ret_value; -} - - -/** - * Generate the "From" value stored in a SQL Database - * - * @access private - * @params string $username username value - * @params string $domain domain value - * @return string - */ -function getIdentityFromSql($username, $domain, $identity, $encode = true) { - $ret_value = $username; - - $dbh = $sth = $record = null; - try { - $dbh = new PDO(IMAP_FROM_SQL_DSN, IMAP_FROM_SQL_USER, IMAP_FROM_SQL_PASSWORD, unserialize(IMAP_FROM_SQL_OPTIONS)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Connected to SQL Database")); - - $sql = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_SQL_QUERY)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Searching From with filter: %s", $sql)); - $sth = $dbh->prepare($sql); - $sth->execute(); - $record = $sth->fetch(PDO::FETCH_ASSOC); - if ($record) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - Found entry in SQL Database. Generating From")); - $ret_value = $identity; - foreach (unserialize(IMAP_FROM_SQL_FIELDS) as $field) { - $ret_value = str_replace('#'.$field, $record[$field], $ret_value); - } - if ($encode) { - $ret_value = encodeFrom($ret_value); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromSql() - No entry found in SQL Database")); - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromSql() - Error getting From value from SQL Database: %s", $ex)); - } - - $dbh = $sth = $record = null; - - return $ret_value; -} - -/** - * Generate the "From" value from the local posix passwd database - * - * @access private - * @params string $username username value - * @params string $domain domain value - * @return string - */ -function getIdentityFromPasswd($username, $domain, $identity, $encode = true) { - $ret_value = $username; - - try { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromPasswd() - Fetching info for user %s", $username)); - - $local_user = posix_getpwnam($username); - if ($local_user) { - $tmp = $local_user['gecos']; - $tmp = explode(',', $tmp); - $name = $tmp[0]; - unset($tmp); - - switch ($identity) { - case 'FROM': - if (strlen($domain) > 0) { - $ret_value = sprintf("%s <%s@%s>", $name, $username, $domain); - } else { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromPasswd() - No domain passed. Cannot construct From address.")); - } - break; - case 'FULLNAME': - $ret_value = sprintf("%s", $name); - break; - } - if ($encode) { - $ret_value = encodeFrom($ret_value); - } - } else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->getIdentityFromPasswd() - No entry found in Password database")); - } - } - catch(Exception $ex) { - ZLog::Write(LOGLEVEL_WARN, sprintf("BackendIMAP->getIdentityFromPasswd() - Error getting From value from passwd database: %s", $ex)); - } - - return $ret_value; -} - - -/** - * Encode the From value as Base64 - * - * @access private - * @param string $from From value - * @return string - */ -function encodeFrom($from) { - $items = explode("<", $from); - $name = trim($items[0]); - return "=?UTF-8?B?" . base64_encode($name) . "?= <" . $items[1]; -} \ No newline at end of file diff --git a/sources/backend/ldap/AUTHOR b/sources/backend/ldap/AUTHOR deleted file mode 100644 index af8002c..0000000 --- a/sources/backend/ldap/AUTHOR +++ /dev/null @@ -1,3 +0,0 @@ -The Author of this backend is dupondje, I could have modified it. -You can found the original code here: -https://github.com/dupondje/PHP-Push-2 diff --git a/sources/backend/ldap/config.php b/sources/backend/ldap/config.php deleted file mode 100644 index 60bbda6..0000000 --- a/sources/backend/ldap/config.php +++ /dev/null @@ -1,58 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ********************** -// BackendLDAP settings -// ********************** - -// Server address -define('LDAP_SERVER', 'localhost'); - -// Server Port -define('LDAP_SERVER_PORT', '389'); - -// LDAP USER DN -define('LDAP_USER_DN', 'uid=%u,ou=mailaccount,dc=phppush,dc=com'); - -// LDAP BASE DNS -define('LDAP_BASE_DNS', 'Contacts:ou=addressbook,uid=%u,ou=mailaccount,dc=phppush,dc=com'); //Multiple values separator is | \ No newline at end of file diff --git a/sources/backend/ldap/ldap.php b/sources/backend/ldap/ldap.php deleted file mode 100644 index af1584b..0000000 --- a/sources/backend/ldap/ldap.php +++ /dev/null @@ -1,590 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/ldap/config.php"); - -class BackendLDAP extends BackendDiff { - - private $ldap_link; - private $user; - - public function Logon($username, $domain, $password) { - $this->user = $username; - $user_dn = str_replace('%u', $username, LDAP_USER_DN); - $this->ldap_link = ldap_connect(LDAP_SERVER, LDAP_SERVER_PORT); - ldap_set_option($this->ldap_link, LDAP_OPT_PROTOCOL_VERSION, 3); - if (ldap_bind($this->ldap_link, $user_dn, $password)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logon(): User '%s' is authenticated on LDAP", $username)); - return true; - } - else { - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logon(): User '%s' is not authenticated on LDAP. Error: ", $username, ldap_error($this->ldap_link))); - return false; - } - } - - public function Logoff() { - if (ldap_unbind($this->ldap_link)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logoff(): Disconnection successfull.")); - } - else { - ZLog::Write(LOGLEVEL_INFO, sprintf("BackendLDAP->Logoff(): Disconnection failed. Error: %s", ldap_error($this->ldap_link))); - } - return true; - } - - public function SendMail($sm) { - return false; - } - - public function GetAttachmentData($attname) { - return false; - } - - public function GetWasteBasket() { - return false; - } - - public function GetFolderList() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetFolderList(): Getting all folders.")); - $contacts = array(); - $dns = explode("|", LDAP_BASE_DNS); - foreach ($dns as $dn) { - $name = explode(":", $dn); - $folder = $this->StatFolder($name[0]); - $contacts[] = $folder; - } - return $contacts; - } - - public function GetFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetFolder('%s')", $id)); - $folder = new SyncFolder(); - $folder->serverid = $id; - $folder->parentid = "0"; - $folder->displayname = $id; - $folder->type = SYNC_FOLDER_TYPE_CONTACT; - return $folder; - } - - public function StatFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->StatFolder('%s')", $id)); - $folder = $this->GetFolder($id); - $stat = array(); - $stat["id"] = $id; - $stat["parent"] = $folder->parentid; - $stat["mod"] = $folder->displayname; - return $stat; - } - - public function ChangeFolder($folderid, $oldid, $displayname, $type) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->ChangeFolder('%s','%s','%s','%s')", $folderid, $oldid, $displayname, $type)); - return false; - } - - public function DeleteFolder($id, $parentid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->DeleteFolder('%s','%s')", $id, $parentid)); - return false; - } - - public function GetMessageList($folderid, $cutoffdate) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessageList('%s','%s')", $folderid, $cutoffdate)); - - $cutoff = date("YmdHis\Z", $cutoffdate); - $filter = sprintf('(modifyTimestamp>=%s)', $cutoff); - $attributes = array("entryUUID", "modifyTimestamp"); - $messages = array(); - - $base_dns = explode("|", LDAP_BASE_DNS); - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $base_dn = str_replace('%u', $this->user, $folder[1]); - $results = ldap_list($this->ldap_link, $base_dn, $filter, $attributes); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessageList(): Got %s contacts in base_dn '%s'.", ldap_count_entries($this->ldap_link, $results), $base_dn)); - $entries = ldap_get_entries($this->ldap_link, $results); - for ($i = 0; $i < $entries["count"]; $i++) { - $message = array(); - $message["id"] = $entries[$i]["entryuuid"][0]; - $message["mod"] = $entries[$i]["modifytimestamp"][0]; - $message["flags"] = "1"; - $messages[] = $message; - } - } - } - return $messages; - } - - public function GetMessage($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->GetMessage('%s','%s')", $folderid, $id)); - - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - $base_dns = explode("|", LDAP_BASE_DNS); - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $base_dn = str_replace('%u', $this->user, $folder[1]); - $result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")"); - if ($result_id) { - $entry_id = ldap_first_entry($this->ldap_link, $result_id); - if ($entry_id) { - return $this->_ParseLDAPMessage($result_id, $entry_id, $truncsize); - } - } - } - } - } - - private function _ParseLDAPMessage($result_id, $entry_id, $truncsize = -1) { - $contact = new SyncContact(); - - $values = ldap_get_attributes($this->ldap_link, $entry_id); - for ($i = 0; $i < $values["count"]; $i++) { - $name = $values[$i]; - $value = $values[$name][0]; - - switch ($name) { - //person - case "cn": - case "fileAs": - $contact->fileas = $value; - break; - case "sn": - $contact->lastname = $value; - break; - //inetOrgPerson - case "departmentNumber": - $contact->department = $value; - break; - case "givenName": - $contact->firstname = $value; - break; - case "homePhone": - $contact->homephonenumber = $value; - if ($values[$name]["count"] >= 2) { - $contact->home2phonenumber = $values[$name][1]; - } - break; - case "jpegPhoto": - $contact->picture = base64_encode($value); - break; - case "labeledURI": - $contact->webpage = $value; - break; - case "mail": - $contact->email1address = $value; - if ($values[$name]["count"] >= 2) { - $contact->email2address = $values[$name][1]; - } - if ($values[$name]["count"] >= 3) { - $contact->email3address = $values[$name][2]; - } - break; - case "mobile": - $contact->mobilephonenumber = $value; - break; - case "o": - $contact->companyname = $value; - break; - case "pager": - $contact->pagernumber = $value; - break; - case "secretary": - case "assistantName": - $contact->assistantname = $value; - break; - //organizationalPerson - case "l": - $contact->businesscity = $value; - break; - case "ou": - $contact->department = $value; - break; - case "physicalDeliveryOfficeName": - $contact->officelocation = $value; - break; - case "postalCode": - $contact->businesspostalcode = $value; - break; - case "st": - $contact->businessstate = $value; - break; - case "street": - $contact->businessstreet = $value; - break; - case "telephoneNumber": - $contact->businessphonenumber = $value; - if ($values[$name]["count"] >= 2) { - $contact->business2phonenumber = $values[$name][1]; - } - break; - case "title": - $contact->title = $value; - break; - case "description": - case "note": - if (Request::GetProtocolVersion() >= 12.0) { - $contact->asbody = new SyncBaseBody(); - $contact->asbody->type = SYNC_BODYPREFERENCE_PLAIN; - $contact->asbody->data = $value; - if ($truncsize > 0 && $truncsize < strlen($contact->asbody->data)) { - $contact->asbody->truncated = 1; - $contact->asbody->data = Utils::Utf8_truncate($contact->asbody->data, $truncsize); - } - else { - $contact->asbody->truncated = 0; - } - $contact->asbody->estimatedDataSize = strlen($contact->asbody->data); - } - else { - $contact->body = $value; - if ($truncsize > 0 && $truncsize < strlen($contact->body)) { - $contact->bodytruncated = 1; - $contact->body = Utils::Utf8_truncate($contact->body, $truncsize); - } - else { - $contact->bodytruncated = 0; - } - $contact->bodysize = strlen($contact->body); - } - break; - case "assistantPhone": - $contact->assistnamephonenumber = $value; - break; - case "birthDate": - $contact->birthday = $value; - break; - case "anniversary": - $contact->anniversary = $value; - break; - case "businessRole": - $contact->jobtitle = $value; - break; - case "carPhone": - $contact->carphonenumber = $value; - break; - case "facsimileTelephoneNumber": - $contact->businessfaxnumber = $value; - break; - case "homeFacsimileTelephoneNumber": - $contact->homefaxnumber = $value; - break; - case "spouseName": - $contact->spouse = $value; - break; - case "managerName": - $contact->managername = $value; - break; - case "radio": - $contact->radiophonenumber = $value; - break; - } - } - return $contact; - } - - public function StatMessage($folderid, $id) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->StatMessage('%s','%s')", $folderid, $id)); - $base_dns = explode("|", LDAP_BASE_DNS); - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $base_dn = str_replace('%u', $this->user, $folder[1]); - $result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("modifyTimestamp")); - if ($result_id) { - $entry_id = ldap_first_entry($this->ldap_link, $result_id); - if ($entry_id) { - $mod = ldap_get_values($this->ldap_link, $entry_id, "modifyTimestamp"); - $message = array(); - $message["id"] = $id; - $message["mod"] = $mod[0]; - $message["flags"] = "1"; - return $message; - } - } - } - } - } - - public function ChangeMessage($folderid, $id, $message, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->ChangeMessage('%s','%s')", $folderid, $id)); - $base_dns = explode("|", LDAP_BASE_DNS); - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $base_dn = str_replace('%u', $this->user, $folder[1]); - $ldap_attributes = $this->_GenerateLDAPArray($message); - $result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("modifyTimestamp")); - if ($result_id) { - $entry_id = ldap_first_entry($this->ldap_link, $result_id); - if ($entry_id) { - $dn = ldap_get_dn($this->ldap_link, $entry_id); - - // We cannot ldap_modify objectClass, but we can use ldap_mod_replace - $ldap_classes = array(); - $ldap_classes['objectclass'] = Array("top", "person", "inetOrgPerson", "organizationalPerson", "evolutionPerson"); - $mode = ldap_mod_replace($this->ldap_link, $dn, $ldap_classes); - - $mod = ldap_modify($this->ldap_link, $dn, $ldap_attributes); - if (!$mod) { - return false; - } - return $this->StatMessage($folderid, $id); - } - else { - $uid = time() . mt_rand(100000, 999999); - $dn = "uid=" . $uid . "," . $base_dn; - $add = ldap_add($this->ldap_link, $dn, $ldap_attributes); - if (!$add) { - return false; - } - $result = ldap_read($this->ldap_link, $dn, "objectClass=*", array("entryUUID")); - $entry = ldap_first_entry($this->ldap_link, $result); - $values = ldap_get_values($this->ldap_link, $entry, "entryUUID"); - $entryuuid = $values[0]; - return $this->StatMessage($folderid, $entryuuid); - } - } - } - } - return false; - } - - private function _GenerateLDAPArray($message) { - $ldap = array(); - //Set the Object Class - $ldap["objectClass"] = Array("top", "person", "inetOrgPerson", "organizationalPerson", "evolutionPerson"); - - //Parse Data - if ($message->fileas) { - $ldap["cn"] = $message->fileas; - $ldap["fileAs"] = $message->fileas; - } - if ($message->lastname) { - $ldap["sn"] = $message->lastname; - } - if ($message->department) { - $ldap["departmentNumber"] = $message->department; - } - if ($message->firstname) { - $ldap["givenName"] = $message->firstname; - } - if ($message->homephonenumber) { - $ldap["homePhone"][0] = $message->homephonenumber; - } - if ($message->home2phonenumber) { - $ldap["homePhone"][1] = $message->home2phonenumber; - } - if ($message->picture) { - $ldap["jpegPhoto"] = base64_decode($message->picture); - } - if ($message->webpage) { - $ldap["labeledURI"] = $message->webpage; - } - if ($message->email1address) { - $ldap["mail"][] = $message->email1address; - } - if ($message->email2address) { - $ldap["mail"][] = $message->email2address; - } - if ($message->email3address) { - $ldap["mail"][] = $message->email3address; - } - if ($message->mobilephonenumber) { - $ldap["mobile"] = $message->mobilephonenumber; - } - if ($message->companyname) { - $ldap["o"] = $message->companyname; - } - if ($message->pagernumber) { - $ldap["pager"] = $message->pagernumber; - } - if ($message->assistantname) { - $ldap["secretary"] = $message->assistantname; - $ldap["assistantName"] = $message->assistantname; - } - if ($message->businesscity) { - $ldap["l"] = $message->businesscity; - } - if ($message->department) { - $ldap["ou"] = $message->department; - } - if ($message->officelocation) { - $ldap["physicalDeliveryOfficeName"] = $message->officelocation; - } - if ($message->businesspostalcode) { - $ldap["postalCode"] = $message->businesspostalcode; - } - if ($message->businessstate) { - $ldap["st"] = $message->businessstate; - } - if ($message->businessstreet) { - $ldap["street"] = $message->businessstreet; - } - if ($message->businessphonenumber) { - $ldap["telephoneNumber"][] = $message->businessphonenumber; - } - if ($message->business2phonenumber) { - $ldap["telephoneNumber"][] = $message->business2phonenumber; - } - if ($message->title) { - $ldap["title"] = $message->title; - } - if ($message->body) { - $ldap["description"] = $message->body; - } - if ($message->asbody) { - $ldap["description"] = $message->asbody->data; - } - if ($message->assistnamephonenumber) { - $ldap["assistantPhone"] = $message->assistnamephonenumber; - } - if ($message->birthday) { - $ldap["birthDate"] = $message->birthday; - } - if ($message->anniversary) { - $ldap["anniversary"] = $message->anniversary; - } - if ($message->jobtitle) { - $ldap["businessRole"] = $message->jobtitle; - } - if ($message->carphonenumber) { - $ldap["carPhone"] = $message->carphonenumber; - } - if ($message->businessfaxnumber) { - $ldap["facsimileTelephoneNumber"] = $message->businessfaxnumber; - } - if ($message->homefaxnumber) { - $ldap["homeFacsimileTelephoneNumber"] = $message->homefaxnumber; - } - if ($message->spouse) { - $ldap["spouseName"] = $message->spouse; - } - if ($message->managername) { - $ldap["managerName"] = $message->managername; - } - if ($message->radiophonenumber) { - $ldap["radio"] = $message->radiophonenumber; - } - - return $ldap; - } - - public function SetReadFlag($folderid, $id, $flags, $contentParameters) { - return false; - } - - public function DeleteMessage($folderid, $id, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->DeleteMessage('%s','%s')", $folderid, $id)); - $base_dns = explode("|", LDAP_BASE_DNS); - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $base_dn = str_replace('%u', $this->user, $folder[1]); - $result_id = ldap_list($this->ldap_link, $base_dn, "(entryUUID=".$id.")", array("entryUUID")); - if ($result_id) { - $entry_id = ldap_first_entry($this->ldap_link, $result_id); - if ($entry_id) { - $dn = ldap_get_dn($this->ldap_link, $entry_id); - return ldap_delete($this->ldap_link, $dn); - } - } - } - } - return false; - } - - public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendLDAP->MoveMessage('%s','%s', '%s')", $folderid, $id, $newfolderid)); - $base_dns = explode("|", LDAP_BASE_DNS); - $old = ""; - $new = ""; - foreach ($base_dns as $base_dn) { - $folder = explode(":", $base_dn); - if ($folder[0] == $folderid) { - $old = str_replace('%u', $this->user, $folder[1]); - } - if ($folder[0] == $newfolderid) { - $new = str_replace('%u', $this->user, $folder[1]); - } - } - $result_id = ldap_list($this->ldap_link, $old, "(entryUUID=".$id.")", array("entryUUID")); - if ($result_id) { - $entry_id = ldap_first_entry($this->ldap_link, $result_id); - if ($entry_id) { - $dn = ldap_get_dn($this->ldap_link, $entry_id); - $newdn = ldap_explode_dn($dn, 0); - return ldap_rename($this->ldap_link, $dn, $newdn[0], true); - } - } - return false; - } - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - // TODO: - return false; - } - - /** - * Indicates which AS version is supported by the backend. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_14; - } -} \ No newline at end of file diff --git a/sources/backend/maildir/config.php b/sources/backend/maildir/config.php deleted file mode 100644 index 154ee56..0000000 --- a/sources/backend/maildir/config.php +++ /dev/null @@ -1,49 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ************************ -// BackendMaildir settings -// ************************ - -define('MAILDIR_BASE', '/tmp'); -define('MAILDIR_SUBDIR', 'Maildir'); diff --git a/sources/backend/maildir/maildir.php b/sources/backend/maildir/maildir.php deleted file mode 100644 index b4f1435..0000000 --- a/sources/backend/maildir/maildir.php +++ /dev/null @@ -1,716 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/maildir/config.php"); - -class BackendMaildir extends BackendDiff { - /**---------------------------------------------------------------------------------------------------------- - * default backend methods - */ - - /** - * Authenticates the user - NOT EFFECTIVELY IMPLEMENTED - * Normally some kind of password check would be done here. - * Alternatively, the password could be ignored and an Apache - * authentication via mod_auth_* could be done - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - */ - public function Logon($username, $domain, $password) { - return true; - } - - /** - * Logs off - * - * @access public - * @return boolean - */ - public function Logoff() { - return true; - } - - /** - * Sends an e-mail - * Not implemented here - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - return false; - } - - /** - * Returns the waste basket - * - * @access public - * @return string - */ - public function GetWasteBasket() { - return false; - } - - /** - * Returns the content of the named attachment as stream. The passed attachment identifier is - * the exact string that is returned in the 'AttName' property of an SyncAttachment. - * Any information necessary to find the attachment must be encoded in that 'attname' property. - * Data is written directly (with print $data;) - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - list($id, $part) = explode(":", $attname); - - $fn = $this->findMessage($id); - if ($fn == false) - throw new StatusException(sprintf("BackendMaildir->GetAttachmentData('%s'): Error, requested message/attachment can not be found", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - // Parse e-mail - $rfc822 = file_get_contents($this->getPath() . "/$fn"); - - $message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8')); - - $attachment = new SyncItemOperationsAttachment(); - $attachment->data = StringStreamWrapper::Open($message->parts[$part]->body); - if (isset($message->parts[$part]->ctype_primary) && isset($message->parts[$part]->ctype_secondary)) - $attachment->contenttype = $message->parts[$part]->ctype_primary .'/'.$message->parts[$part]->ctype_secondary; - - return $attachment; - } - - /**---------------------------------------------------------------------------------------------------------- - * implemented DiffBackend methods - */ - - - /** - * Returns a list (array) of folders. - * In simple implementations like this one, probably just one folder is returned. - * - * @access public - * @return array - */ - public function GetFolderList() { - $folders = array(); - - $inbox = array(); - $inbox["id"] = "root"; - $inbox["parent"] = "0"; - $inbox["mod"] = "Inbox"; - - $folders[]=$inbox; - - $sub = array(); - $sub["id"] = "sub"; - $sub["parent"] = "root"; - $sub["mod"] = "Sub"; - -// $folders[]=$sub; - - return $folders; - } - - /** - * Returns an actual SyncFolder object - * - * @param string $id id of the folder - * - * @access public - * @return object SyncFolder with information - */ - public function GetFolder($id) { - if($id == "root") { - $inbox = new SyncFolder(); - - $inbox->serverid = $id; - $inbox->parentid = "0"; // Root - $inbox->displayname = "Inbox"; - $inbox->type = SYNC_FOLDER_TYPE_INBOX; - - return $inbox; - } else if($id == "sub") { - $inbox = new SyncFolder(); - $inbox->serverid = $id; - $inbox->parentid = "root"; - $inbox->displayname = "Sub"; - $inbox->type = SYNC_FOLDER_TYPE_OTHER; - - return $inbox; - } else { - return false; - } - } - - - /** - * Returns folder stats. An associative array with properties is expected. - * - * @param string $id id of the folder - * - * @access public - * @return array - */ - public function StatFolder($id) { - $folder = $this->GetFolder($id); - - $stat = array(); - $stat["id"] = $id; - $stat["parent"] = $folder->parentid; - $stat["mod"] = $folder->displayname; - - return $stat; - } - - - /** - * Creates or modifies a folder - * not implemented - * - * @param string $folderid id of the parent folder - * @param string $oldid if empty -> new folder created, else folder is to be renamed - * @param string $displayname new folder name (to be created, or to be renamed to) - * @param int $type folder type - * - * @access public - * @return boolean status - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function ChangeFolder($folderid, $oldid, $displayname, $type){ - return false; - } - - /** - * Deletes a folder - * - * @param string $id - * @param string $parent is normally false - * - * @access public - * @return boolean status - false if e.g. does not exist - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function DeleteFolder($id, $parentid){ - return false; - } - - /** - * Returns a list (array) of messages - * - * @param string $folderid id of the parent folder - * @param long $cutoffdate timestamp in the past from which on messages should be returned - * - * @access public - * @return array/false array with messages or false if folder is not available - */ - public function GetMessageList($folderid, $cutoffdate) { - $this->moveNewToCur(); - - if($folderid != "root") - return false; - - // return stats of all messages in a dir. We can do this faster than - // just calling statMessage() on each message; We still need fstat() - // information though, so listing 10000 messages is going to be - // rather slow (depending on filesystem, etc) - - // we also have to filter by the specified cutoffdate so only the - // last X days are retrieved. Normally, this would mean that we'd - // have to open each message, get the Received: header, and check - // whether that is in the filter range. Because this is much too slow, we - // are depending on the creation date of the message instead, which should - // normally be just about the same, unless you just did some kind of import. - - $messages = array(); - $dirname = $this->getPath(); - - $dir = opendir($dirname); - - if(!$dir) - return false; - - while($entry = readdir($dir)) { - if($entry{0} == ".") - continue; - - $message = array(); - - $stat = stat("$dirname/$entry"); - - if($stat["mtime"] < $cutoffdate) { - // message is out of range for curoffdate, ignore it - continue; - } - - $message["mod"] = $stat["mtime"]; - - $matches = array(); - - // Flags according to http://cr.yp.to/proto/maildir.html (pretty authoritative - qmail author's website) - if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$entry,$matches)) - continue; - $message["id"] = $matches[1]; - $message["flags"] = 0; - - if(strpos($matches[2],"S") !== false) { - $message["flags"] |= 1; // 'seen' aka 'read' is the only flag we want to know about - } - - array_push($messages, $message); - } - - return $messages; - } - - /** - * Returns the actual SyncXXX object type. - * - * @param string $folderid id of the parent folder - * @param string $id id of the message - * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) - * - * @access public - * @return object/false false if the message could not be retrieved - */ - public function GetMessage($folderid, $id, $truncsize, $mimesupport = 0) { - if($folderid != 'root') - return false; - - $fn = $this->findMessage($id); - - // Get flags, etc - $stat = $this->StatMessage($folderid, $id); - - // Parse e-mail - $rfc822 = file_get_contents($this->getPath() . "/" . $fn); - - $message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8')); - - $output = new SyncMail(); - - $output->body = str_replace("\n", "\r\n", $this->getBody($message)); - $output->bodysize = strlen($output->body); - $output->bodytruncated = 0; // We don't implement truncation in this backend - $output->datereceived = $this->parseReceivedDate($message->headers["received"][0]); - $output->messageclass = "IPM.Note"; - $output->subject = $message->headers["subject"]; - $output->read = $stat["flags"]; - $output->from = $message->headers["from"]; - - $Mail_RFC822 = new Mail_RFC822(); - $toaddr = $ccaddr = $replytoaddr = array(); - if(isset($message->headers["to"])) - $toaddr = $Mail_RFC822->parseAddressList($message->headers["to"]); - if(isset($message->headers["cc"])) - $ccaddr = $Mail_RFC822->parseAddressList($message->headers["cc"]); - if(isset($message->headers["reply_to"])) - $replytoaddr = $Mail_RFC822->parseAddressList($message->headers["reply_to"]); - - $output->to = array(); - $output->cc = array(); - $output->reply_to = array(); - foreach(array("to" => $toaddr, "cc" => $ccaddr, "reply_to" => $replytoaddr) as $type => $addrlist) { - foreach($addrlist as $addr) { - $address = $addr->mailbox . "@" . $addr->host; - $name = $addr->personal; - - if (!isset($output->displayto) && $name != "") - $output->displayto = $name; - - if($name == "" || $name == $address) - $fulladdr = w2u($address); - else { - if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') { - $fulladdr = "\"" . w2u($name) ."\" <" . w2u($address) . ">"; - } - else { - $fulladdr = w2u($name) ." <" . w2u($address) . ">"; - } - } - - array_push($output->$type, $fulladdr); - } - } - - // convert mime-importance to AS-importance - if (isset($message->headers["x-priority"])) { - $mimeImportance = preg_replace("/\D+/", "", $message->headers["x-priority"]); - if ($mimeImportance > 3) - $output->importance = 0; - if ($mimeImportance == 3) - $output->importance = 1; - if ($mimeImportance < 3) - $output->importance = 2; - } - - // Attachments are only searched in the top-level part - $n = 0; - if(isset($message->parts)) { - foreach($message->parts as $part) { - if($part->ctype_primary == "application") { - $attachment = new SyncAttachment(); - $attachment->attsize = strlen($part->body); - - if(isset($part->d_parameters['filename'])) - $attname = $part->d_parameters['filename']; - else if(isset($part->ctype_parameters['name'])) - $attname = $part->ctype_parameters['name']; - else if(isset($part->headers['content-description'])) - $attname = $part->headers['content-description']; - else $attname = "unknown attachment"; - - $attachment->displayname = $attname; - $attachment->attname = $id . ":" . $n; - $attachment->attmethod = 1; - $attachment->attoid = isset($part->headers['content-id']) ? $part->headers['content-id'] : ""; - - array_push($output->attachments, $attachment); - } - $n++; - } - } - - return $output; - } - - /** - * Returns message stats, analogous to the folder stats from StatFolder(). - * - * @param string $folderid id of the folder - * @param string $id id of the message - * - * @access public - * @return array - */ - public function StatMessage($folderid, $id) { - $dirname = $this->getPath(); - $fn = $this->findMessage($id); - if(!$fn) - return false; - - $stat = stat("$dirname/$fn"); - - $entry = array(); - $entry["id"] = $id; - $entry["flags"] = 0; - - if(strpos($fn,"S")) - $entry["flags"] |= 1; - $entry["mod"] = $stat["mtime"]; - - return $entry; - } - - /** - * Called when a message has been changed on the mobile. - * This functionality is not available for emails. - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param SyncXXX $message the SyncObject containing a message - * @param ContentParameters $contentParameters - * - * @access public - * @return array same return value as StatMessage() - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function ChangeMessage($folderid, $id, $message, $contentParameters) { - // TODO SyncInterval check + ContentParameters - // see https://jira.zarafa.com/browse/ZP-258 for details - // before changing the message, it should be checked if the message is in the SyncInterval - // to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType()); - // if the message is not in the interval an StatusException with code SYNC_STATUS_SYNCCANNOTBECOMPLETED should be thrown - return false; - } - - /** - * Changes the 'read' flag of a message on disk - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param int $flags read flag of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function SetReadFlag($folderid, $id, $flags, $contentParameters) { - if($folderid != 'root') - return false; - - // TODO SyncInterval check + ContentParameters - // see https://jira.zarafa.com/browse/ZP-258 for details - // before setting the read flag, it should be checked if the message is in the SyncInterval - // to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType()); - // if the message is not in the interval an StatusException with code SYNC_STATUS_OBJECTNOTFOUND should be thrown - - $fn = $this->findMessage($id); - - if(!$fn) - return true; // message may have been deleted - - if(!preg_match("/([^:]+):2,([PRSTDF]*)/",$fn,$matches)) - return false; - - // remove 'seen' (S) flag - if(!$flags) { - $newflags = str_replace("S","",$matches[2]); - } else { - // make sure we don't double add the 'S' flag - $newflags = str_replace("S","",$matches[2]) . "S"; - } - - $newfn = $matches[1] . ":2," . $newflags; - // rename if required - if($fn != $newfn) - rename($this->getPath() ."/$fn", $this->getPath() . "/$newfn"); - - return true; - } - - /** - * Called when the user has requested to delete (really delete) a message - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function DeleteMessage($folderid, $id, $contentParameters) { - if($folderid != 'root') - return false; - - // TODO SyncInterval check + ContentParameters - // see https://jira.zarafa.com/browse/ZP-258 for details - // before deleting the message, it should be checked if the message is in the SyncInterval - // to determine the cutoffdate use Utils::GetCutOffDate($contentparameters->GetFilterType()); - // if the message is not in the interval an StatusException with code SYNC_STATUS_OBJECTNOTFOUND should be thrown - - $fn = $this->findMessage($id); - - if(!$fn) - return true; // success because message has been deleted already - - if(!unlink($this->getPath() . "/$fn")) { - return true; // success - message may have been deleted in the mean time (since findMessage) - } - - return true; - } - - /** - * Called when the user moves an item on the PDA from one folder to another - * not implemented - * - * @param string $folderid id of the source folder - * @param string $id id of the message - * @param string $newfolderid id of the destination folder - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions - */ - public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) { - return false; - } - - - /**---------------------------------------------------------------------------------------------------------- - * private maildir-specific internals - */ - - /** - * Searches for the message - * - * @param string $id id of the message - * - * @access private - * @return string - */ - private function findMessage($id) { - // We could use 'this->folderid' for path info but we currently - // only support a single INBOX. We also have to use a glob '*' - // because we don't know the flags of the message we're looking for. - - $dirname = $this->getPath(); - $dir = opendir($dirname); - - while($entry = readdir($dir)) { - if(strpos($entry,$id) === 0) - return $entry; - } - return false; // not found - } - - /** - * Parses the message and return only the plaintext body - * - * @param string $message html message - * - * @access private - * @return string plaintext message - */ - private function getBody($message) { - $body = ""; - $htmlbody = ""; - - $this->getBodyRecursive($message, "plain", $body); - - if(!isset($body) || $body === "") { - $this->getBodyRecursive($message, "html", $body); - // remove css-style tags - $body = preg_replace("//is", "", $body); - // remove all other html - $body = strip_tags($body); - } - - return $body; - } - - /** - * Get all parts in the message with specified type and concatenate them together, unless the - * Content-Disposition is 'attachment', in which case the text is apparently an attachment - * - * @param string $message mimedecode message(part) - * @param string $message message subtype - * @param string &$body body reference - * - * @access private - * @return - */ - private function getBodyRecursive($message, $subtype, &$body) { - if(!isset($message->ctype_primary)) return; - if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body)) - $body .= $message->body; - - if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) { - foreach($message->parts as $part) { - if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment")) { - $this->getBodyRecursive($part, $subtype, $body); - } - } - } - } - - /** - * Parses the received date - * - * @param string $received received date string - * - * @access private - * @return long - */ - private function parseReceivedDate($received) { - $pos = strpos($received, ";"); - if(!$pos) - return false; - - $datestr = substr($received, $pos+1); - $datestr = ltrim($datestr); - - return strtotime($datestr); - } - - /** - * Moves everything in Maildir/new/* to Maildir/cur/ - * - * @access private - * @return - */ - private function moveNewToCur() { - $newdirname = MAILDIR_BASE . "/" . $this->store . "/" . MAILDIR_SUBDIR . "/new"; - - $newdir = opendir($newdirname); - - while($newentry = readdir($newdir)) { - if($newentry{0} == ".") - continue; - - // link/unlink == move. This is the way to move the message according to cr.yp.to - link($newdirname . "/" . $newentry, $this->getPath() . "/" . $newentry . ":2,"); - unlink($newdirname . "/" . $newentry); - } - } - - /** - * The path we're working on - * - * @access private - * @return string - */ - private function getPath() { - return MAILDIR_BASE . "/" . $this->store . "/" . MAILDIR_SUBDIR . "/cur"; - } -} \ No newline at end of file diff --git a/sources/backend/searchldap/config.php b/sources/backend/searchldap/config.php deleted file mode 100644 index 134990a..0000000 --- a/sources/backend/searchldap/config.php +++ /dev/null @@ -1,74 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// LDAP host and port -define("LDAP_HOST", "ldap://127.0.0.1/"); -define("LDAP_PORT", "389"); - -// Set USER and PASSWORD if not using anonymous bind -define("ANONYMOUS_BIND", true); -define("LDAP_BIND_USER", "cn=searchuser,dc=test,dc=net"); -define("LDAP_BIND_PASSWORD", ""); - -// Search base & filter -// the SEARCHVALUE string is substituded by the value inserted into the search field -define("LDAP_SEARCH_BASE", "ou=global,dc=test,dc=net"); -define("LDAP_SEARCH_FILTER", "(|(cn=*SEARCHVALUE*)(mail=*SEARCHVALUE*))"); - -// LDAP field mapping. -// values correspond to an inetOrgPerson class -global $ldap_field_map; -$ldap_field_map = array( - SYNC_GAL_DISPLAYNAME => 'cn', - SYNC_GAL_PHONE => 'telephonenumber', - SYNC_GAL_OFFICE => '', - SYNC_GAL_TITLE => 'title', - SYNC_GAL_COMPANY => 'ou', - SYNC_GAL_ALIAS => 'uid', - SYNC_GAL_FIRSTNAME => 'givenname', - SYNC_GAL_LASTNAME => 'sn', - SYNC_GAL_HOMEPHONE => 'homephone', - SYNC_GAL_MOBILEPHONE => 'mobile', - SYNC_GAL_EMAILADDRESS => 'mail', - ); diff --git a/sources/backend/searchldap/searchldap.php b/sources/backend/searchldap/searchldap.php deleted file mode 100644 index f9bcd9f..0000000 --- a/sources/backend/searchldap/searchldap.php +++ /dev/null @@ -1,197 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -require_once("backend/searchldap/config.php"); - -class BackendSearchLDAP implements ISearchProvider { - private $connection; - - /** - * Initializes the backend to perform the search - * Connects to the LDAP server using the values from the configuration - * - * - * @access public - * @return - * @throws StatusException - */ - public function BackendSearchLDAP() { - if (!function_exists("ldap_connect")) - throw new StatusException("BackendSearchLDAP(): php-ldap is not installed. Search aborted.", SYNC_SEARCHSTATUS_STORE_SERVERERROR, null, LOGLEVEL_FATAL); - - // connect to LDAP - $this->connection = @ldap_connect(LDAP_HOST, LDAP_PORT); - @ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3); - - // Authenticate - if (constant('ANONYMOUS_BIND') === true) { - if(! @ldap_bind($this->connection)) { - $this->connection = false; - throw new StatusException("BackendSearchLDAP(): Could not bind anonymously to server! Search aborted.", SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED, null, LOGLEVEL_ERROR); - } - } - else if (constant('LDAP_BIND_USER') != "") { - if(! @ldap_bind($this->connection, LDAP_BIND_USER, LDAP_BIND_PASSWORD)) { - $this->connection = false; - throw new StatusException(sprintf("BackendSearchLDAP(): Could not bind to server with user '%s' and specified password! Search aborted.", LDAP_BIND_USER), SYNC_SEARCHSTATUS_STORE_ACCESSDENIED, null, LOGLEVEL_ERROR); - } - } - else { - // it would be possible to use the users login and password to authenticate on the LDAP server - // the main $backend has to keep these values so they could be used here - $this->connection = false; - throw new StatusException("BackendSearchLDAP(): neither anonymous nor default bind enabled. Other options not implemented.", SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED, null, LOGLEVEL_ERROR); - } - } - - /** - * Indicates if a search type is supported by this SearchProvider - * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - return ($searchtype == ISearchProvider::SEARCH_GAL); - } - - - /** - * Queries the LDAP backend - * - * @param string $searchquery string to be searched for - * @param string $searchrange specified searchrange - * - * @access public - * @return array search results - */ - public function GetGALSearchResults($searchquery, $searchrange) { - global $ldap_field_map; - if (isset($this->connection) && $this->connection !== false) { - $searchfilter = str_replace("SEARCHVALUE", $searchquery, LDAP_SEARCH_FILTER); - $result = @ldap_search($this->connection, LDAP_SEARCH_BASE, $searchfilter); - if (!$result) { - ZLog::Write(LOGLEVEL_ERROR, "BackendSearchLDAP: Error in search query. Search aborted"); - return false; - } - - // get entry data as array - $searchresult = ldap_get_entries($this->connection, $result); - - // range for the search results, default symbian range end is 50, wm 99, - // so we'll use that of nokia - $rangestart = 0; - $rangeend = 50; - - if ($searchrange != '0') { - $pos = strpos($searchrange, '-'); - $rangestart = substr($searchrange, 0, $pos); - $rangeend = substr($searchrange, ($pos + 1)); - } - $items = array(); - - // TODO the limiting of the searchresults could be refactored into Utils as it's probably used more than once - $querycnt = $searchresult['count']; - //do not return more results as requested in range - $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt; - $items['range'] = $rangestart.'-'.($querylimit-1); - $items['searchtotal'] = $querycnt; - - $rc = 0; - for ($i = $rangestart; $i < $querylimit; $i++) { - foreach ($ldap_field_map as $key=>$value ) { - if (isset($searchresult[$i][$value])) { - if (is_array($searchresult[$i][$value])) - $items[$rc][$key] = $searchresult[$i][$value][0]; - else - $items[$rc][$key] = $searchresult[$i][$value]; - } - } - $rc++; - } - - return $items; - } - else - return false; - } - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo) { - return array(); - } - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - return true; - } - - /** - * Disconnects from LDAP - * - * @access public - * @return boolean - */ - public function Disconnect() { - if ($this->connection) - @ldap_close($this->connection); - - return true; - } -} \ No newline at end of file diff --git a/sources/backend/vcarddir/config.php b/sources/backend/vcarddir/config.php deleted file mode 100644 index e841c56..0000000 --- a/sources/backend/vcarddir/config.php +++ /dev/null @@ -1,48 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ********************** -// BackendVCardDir settings -// ********************** - -define('VCARDDIR_DIR', '/home/%u/.kde/share/apps/kabc/stdvcf'); diff --git a/sources/backend/vcarddir/vcarddir.php b/sources/backend/vcarddir/vcarddir.php deleted file mode 100644 index 4331e26..0000000 --- a/sources/backend/vcarddir/vcarddir.php +++ /dev/null @@ -1,678 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// config file -require_once("backend/vcarddir/config.php"); - -class BackendVCardDir extends BackendDiff { - /**---------------------------------------------------------------------------------------------------------- - * default backend methods - */ - - /** - * Authenticates the user - NOT EFFECTIVELY IMPLEMENTED - * Normally some kind of password check would be done here. - * Alternatively, the password could be ignored and an Apache - * authentication via mod_auth_* could be done - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - */ - public function Logon($username, $domain, $password) { - return true; - } - - /** - * Logs off - * - * @access public - * @return boolean - */ - public function Logoff() { - return true; - } - - /** - * Sends an e-mail - * Not implemented here - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - return false; - } - - /** - * Returns the waste basket - * - * @access public - * @return string - */ - public function GetWasteBasket() { - return false; - } - - /** - * Returns the content of the named attachment as stream - * not implemented - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - return false; - } - - /**---------------------------------------------------------------------------------------------------------- - * implemented DiffBackend methods - */ - - /** - * Returns a list (array) of folders. - * In simple implementations like this one, probably just one folder is returned. - * - * @access public - * @return array - */ - public function GetFolderList() { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::GetFolderList()'); - $contacts = array(); - $folder = $this->StatFolder("root"); - $contacts[] = $folder; - - return $contacts; - } - - /** - * Returns an actual SyncFolder object - * - * @param string $id id of the folder - * - * @access public - * @return object SyncFolder with information - */ - public function GetFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::GetFolder('.$id.')'); - if($id == "root") { - $folder = new SyncFolder(); - $folder->serverid = $id; - $folder->parentid = "0"; - $folder->displayname = "Contacts"; - $folder->type = SYNC_FOLDER_TYPE_CONTACT; - - return $folder; - } else return false; - } - - /** - * Returns folder stats. An associative array with properties is expected. - * - * @param string $id id of the folder - * - * @access public - * @return array - */ - public function StatFolder($id) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::StatFolder('.$id.')'); - $folder = $this->GetFolder($id); - - $stat = array(); - $stat["id"] = $id; - $stat["parent"] = $folder->parentid; - $stat["mod"] = $folder->displayname; - - return $stat; - } - - /** - * Creates or modifies a folder - * not implemented - * - * @param string $folderid id of the parent folder - * @param string $oldid if empty -> new folder created, else folder is to be renamed - * @param string $displayname new folder name (to be created, or to be renamed to) - * @param int $type folder type - * - * @access public - * @return boolean status - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function ChangeFolder($folderid, $oldid, $displayname, $type){ - return false; - } - - /** - * Deletes a folder - * - * @param string $id - * @param string $parent is normally false - * - * @access public - * @return boolean status - false if e.g. does not exist - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public function DeleteFolder($id, $parentid){ - return false; - } - - /** - * Returns a list (array) of messages - * - * @param string $folderid id of the parent folder - * @param long $cutoffdate timestamp in the past from which on messages should be returned - * - * @access public - * @return array/false array with messages or false if folder is not available - */ - public function GetMessageList($folderid, $cutoffdate) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::GetMessageList('.$folderid.')'); - $messages = array(); - - $dir = opendir($this->getPath()); - if(!$dir) - return false; - - while($entry = readdir($dir)) { - if(is_dir($this->getPath() .'/'.$entry)) - continue; - - $message = array(); - $message["id"] = $entry; - $stat = stat($this->getPath() .'/'.$entry); - $message["mod"] = $stat["mtime"]; - $message["flags"] = 1; // always 'read' - - $messages[] = $message; - } - - return $messages; - } - - /** - * Returns the actual SyncXXX object type. - * - * @param string $folderid id of the parent folder - * @param string $id id of the message - * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) - * - * @access public - * @return object/false false if the message could not be retrieved - */ - public function GetMessage($folderid, $id, $contentparameters) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::GetMessage('.$folderid.', '.$id.', ..)'); - if($folderid != "root") - return; - - $types = array ('dom' => 'type', 'intl' => 'type', 'postal' => 'type', 'parcel' => 'type', 'home' => 'type', 'work' => 'type', - 'pref' => 'type', 'voice' => 'type', 'fax' => 'type', 'msg' => 'type', 'cell' => 'type', 'pager' => 'type', - 'bbs' => 'type', 'modem' => 'type', 'car' => 'type', 'isdn' => 'type', 'video' => 'type', - 'aol' => 'type', 'applelink' => 'type', 'attmail' => 'type', 'cis' => 'type', 'eworld' => 'type', - 'internet' => 'type', 'ibmmail' => 'type', 'mcimail' => 'type', - 'powershare' => 'type', 'prodigy' => 'type', 'tlx' => 'type', 'x400' => 'type', - 'gif' => 'type', 'cgm' => 'type', 'wmf' => 'type', 'bmp' => 'type', 'met' => 'type', 'pmb' => 'type', 'dib' => 'type', - 'pict' => 'type', 'tiff' => 'type', 'pdf' => 'type', 'ps' => 'type', 'jpeg' => 'type', 'qtime' => 'type', - 'mpeg' => 'type', 'mpeg2' => 'type', 'avi' => 'type', - 'wave' => 'type', 'aiff' => 'type', 'pcm' => 'type', - 'x509' => 'type', 'pgp' => 'type', 'text' => 'value', 'inline' => 'value', 'url' => 'value', 'cid' => 'value', 'content-id' => 'value', - '7bit' => 'encoding', '8bit' => 'encoding', 'quoted-printable' => 'encoding', 'base64' => 'encoding', - ); - - - // Parse the vcard - $message = new SyncContact(); - - $data = file_get_contents($this->getPath() . "/" . $id); - $data = str_replace("\x00", '', $data); - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", "\n", $data); - $data = preg_replace('/(\n)([ \t])/i', '', $data); - - $lines = explode("\n", $data); - - $vcard = array(); - foreach($lines as $line) { - if (trim($line) == '') - continue; - $pos = strpos($line, ':'); - if ($pos === false) - continue; - - $field = trim(substr($line, 0, $pos)); - $value = trim(substr($line, $pos+1)); - - $fieldparts = preg_split('/(? $v){ - $val[$i] = quoted_printable_decode($v); - } - break; - case 'b': - case 'base64': - foreach($val as $i => $v){ - $val[$i] = base64_decode($v); - } - break; - } - }else{ - foreach($val as $i => $v){ - $val[$i] = $this->unescape($v); - } - } - $fieldvalue['val'] = $val; - $vcard[$type][] = $fieldvalue; - } - - if(isset($vcard['email'][0]['val'][0])) - $message->email1address = $vcard['email'][0]['val'][0]; - if(isset($vcard['email'][1]['val'][0])) - $message->email2address = $vcard['email'][1]['val'][0]; - if(isset($vcard['email'][2]['val'][0])) - $message->email3address = $vcard['email'][2]['val'][0]; - - if(isset($vcard['tel'])){ - foreach($vcard['tel'] as $tel) { - if(!isset($tel['type'])){ - $tel['type'] = array(); - } - if(in_array('car', $tel['type'])){ - $message->carphonenumber = $tel['val'][0]; - }elseif(in_array('pager', $tel['type'])){ - $message->pagernumber = $tel['val'][0]; - }elseif(in_array('cell', $tel['type'])){ - $message->mobilephonenumber = $tel['val'][0]; - }elseif(in_array('home', $tel['type'])){ - if(in_array('fax', $tel['type'])){ - $message->homefaxnumber = $tel['val'][0]; - }elseif(empty($message->homephonenumber)){ - $message->homephonenumber = $tel['val'][0]; - }else{ - $message->home2phonenumber = $tel['val'][0]; - } - }elseif(in_array('work', $tel['type'])){ - if(in_array('fax', $tel['type'])){ - $message->businessfaxnumber = $tel['val'][0]; - }elseif(empty($message->businessphonenumber)){ - $message->businessphonenumber = $tel['val'][0]; - }else{ - $message->business2phonenumber = $tel['val'][0]; - } - }elseif(empty($message->homephonenumber)){ - $message->homephonenumber = $tel['val'][0]; - }elseif(empty($message->home2phonenumber)){ - $message->home2phonenumber = $tel['val'][0]; - }else{ - $message->radiophonenumber = $tel['val'][0]; - } - } - } - //;;street;city;state;postalcode;country - if(isset($vcard['adr'])){ - foreach($vcard['adr'] as $adr) { - if(empty($adr['type'])){ - $a = 'other'; - }elseif(in_array('home', $adr['type'])){ - $a = 'home'; - }elseif(in_array('work', $adr['type'])){ - $a = 'business'; - }else{ - $a = 'other'; - } - if(!empty($adr['val'][2])){ - $b=$a.'street'; - $message->$b = w2ui($adr['val'][2]); - } - if(!empty($adr['val'][3])){ - $b=$a.'city'; - $message->$b = w2ui($adr['val'][3]); - } - if(!empty($adr['val'][4])){ - $b=$a.'state'; - $message->$b = w2ui($adr['val'][4]); - } - if(!empty($adr['val'][5])){ - $b=$a.'postalcode'; - $message->$b = w2ui($adr['val'][5]); - } - if(!empty($adr['val'][6])){ - $b=$a.'country'; - $message->$b = w2ui($adr['val'][6]); - } - } - } - - if(!empty($vcard['fn'][0]['val'][0])) - $message->fileas = w2ui($vcard['fn'][0]['val'][0]); - if(!empty($vcard['n'][0]['val'][0])) - $message->lastname = w2ui($vcard['n'][0]['val'][0]); - if(!empty($vcard['n'][0]['val'][1])) - $message->firstname = w2ui($vcard['n'][0]['val'][1]); - if(!empty($vcard['n'][0]['val'][2])) - $message->middlename = w2ui($vcard['n'][0]['val'][2]); - if(!empty($vcard['n'][0]['val'][3])) - $message->title = w2ui($vcard['n'][0]['val'][3]); - if(!empty($vcard['n'][0]['val'][4])) - $message->suffix = w2ui($vcard['n'][0]['val'][4]); - if(!empty($vcard['bday'][0]['val'][0])){ - $tz = date_default_timezone_get(); - date_default_timezone_set('UTC'); - $message->birthday = strtotime($vcard['bday'][0]['val'][0]); - date_default_timezone_set($tz); - } - if(!empty($vcard['org'][0]['val'][0])) - $message->companyname = w2ui($vcard['org'][0]['val'][0]); - if(!empty($vcard['note'][0]['val'][0])){ - $message->body = w2ui($vcard['note'][0]['val'][0]); - $message->bodysize = strlen($vcard['note'][0]['val'][0]); - $message->bodytruncated = 0; - } - if(!empty($vcard['role'][0]['val'][0])) - $message->jobtitle = w2ui($vcard['role'][0]['val'][0]);//$vcard['title'][0]['val'][0] - if(!empty($vcard['url'][0]['val'][0])) - $message->webpage = w2ui($vcard['url'][0]['val'][0]); - if(!empty($vcard['categories'][0]['val'])) - $message->categories = $vcard['categories'][0]['val']; - - if(!empty($vcard['photo'][0]['val'][0])) - $message->picture = base64_encode($vcard['photo'][0]['val'][0]); - - return $message; - } - - /** - * Returns message stats, analogous to the folder stats from StatFolder(). - * - * @param string $folderid id of the folder - * @param string $id id of the message - * - * @access public - * @return array - */ - public function StatMessage($folderid, $id) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::StatMessage('.$folderid.', '.$id.')'); - if($folderid != "root") - return false; - - $stat = stat($this->getPath() . "/" . $id); - - $message = array(); - $message["mod"] = $stat["mtime"]; - $message["id"] = $id; - $message["flags"] = 1; - - return $message; - } - - /** - * Called when a message has been changed on the mobile. - * This functionality is not available for emails. - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param SyncXXX $message the SyncObject containing a message - * @param ContentParameters $contentParameters - * - * @access public - * @return array same return value as StatMessage() - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function ChangeMessage($folderid, $id, $message, $contentParameters) { - ZLog::Write(LOGLEVEL_DEBUG, 'VCDir::ChangeMessage('.$folderid.', '.$id.', ..)'); - $mapping = array( - 'fileas' => 'FN', - 'lastname;firstname;middlename;title;suffix' => 'N', - 'email1address' => 'EMAIL;INTERNET', - 'email2address' => 'EMAIL;INTERNET', - 'email3address' => 'EMAIL;INTERNET', - 'businessphonenumber' => 'TEL;WORK', - 'business2phonenumber' => 'TEL;WORK', - 'businessfaxnumber' => 'TEL;WORK;FAX', - 'homephonenumber' => 'TEL;HOME', - 'home2phonenumber' => 'TEL;HOME', - 'homefaxnumber' => 'TEL;HOME;FAX', - 'mobilephonenumber' => 'TEL;CELL', - 'carphonenumber' => 'TEL;CAR', - 'pagernumber' => 'TEL;PAGER', - ';;businessstreet;businesscity;businessstate;businesspostalcode;businesscountry' => 'ADR;WORK', - ';;homestreet;homecity;homestate;homepostalcode;homecountry' => 'ADR;HOME', - ';;otherstreet;othercity;otherstate;otherpostalcode;othercountry' => 'ADR', - 'companyname' => 'ORG', - 'body' => 'NOTE', - 'jobtitle' => 'ROLE', - 'webpage' => 'URL', - ); - $data = "BEGIN:VCARD\nVERSION:2.1\nPRODID:Z-Push\n"; - foreach($mapping as $k => $v){ - $val = ''; - $ks = explode(';', $k); - foreach($ks as $i){ - if(!empty($message->$i)) - $val .= $this->escape($message->$i); - $val.=';'; - } - if(empty($val)) - continue; - $val = substr($val,0,-1); - if(strlen($val)>50){ - $data .= $v.":\n\t".substr(chunk_split($val, 50, "\n\t"), 0, -1); - }else{ - $data .= $v.':'.$val."\n"; - } - } - if(!empty($message->categories)) - $data .= 'CATEGORIES:'.implode(',', $this->escape($message->categories))."\n"; - if(!empty($message->picture)) - $data .= 'PHOTO;ENCODING=BASE64;TYPE=JPEG:'."\n\t".substr(chunk_split($message->picture, 50, "\n\t"), 0, -1); - if(isset($message->birthday)) - $data .= 'BDAY:'.date('Y-m-d', $message->birthday)."\n"; - $data .= "END:VCARD"; - -// not supported: anniversary, assistantname, assistnamephonenumber, children, department, officelocation, radiophonenumber, spouse, rtf - - if(!$id){ - if(!empty($message->fileas)){ - $name = u2wi($message->fileas); - }elseif(!empty($message->lastname)){ - $name = $name = u2wi($message->lastname); - }elseif(!empty($message->firstname)){ - $name = $name = u2wi($message->firstname); - }elseif(!empty($message->companyname)){ - $name = $name = u2wi($message->companyname); - }else{ - $name = 'unknown'; - } - $name = preg_replace('/[^a-z0-9 _-]/i', '', $name); - $id = $name.'.vcf'; - $i = 0; - while(file_exists($this->getPath().'/'.$id)){ - $i++; - $id = $name.$i.'.vcf'; - } - } - file_put_contents($this->getPath().'/'.$id, $data); - return $this->StatMessage($folderid, $id); - } - - /** - * Changes the 'read' flag of a message on disk - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param int $flags read flag of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function SetReadFlag($folderid, $id, $flags, $contentParameters) { - return false; - } - - /** - * Called when the user has requested to delete (really delete) a message - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public function DeleteMessage($folderid, $id, $contentParameters) { - return unlink($this->getPath() . '/' . $id); - } - - /** - * Called when the user moves an item on the PDA from one folder to another - * not implemented - * - * @param string $folderid id of the source folder - * @param string $id id of the message - * @param string $newfolderid id of the destination folder - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions - */ - public function MoveMessage($folderid, $id, $newfolderid, $contentParameters) { - return false; - } - - - /**---------------------------------------------------------------------------------------------------------- - * private vcard-specific internals - */ - - /** - * The path we're working on - * - * @access private - * @return string - */ - private function getPath() { - return str_replace('%u', $this->store, VCARDDIR_DIR); - } - - /** - * Escapes a string - * - * @param string $data string to be escaped - * - * @access private - * @return string - */ - function escape($data){ - if (is_array($data)) { - foreach ($data as $key => $val) { - $data[$key] = $this->escape($val); - } - return $data; - } - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", "\n", $data); - $data = str_replace(array('\\', ';', ',', "\n"), array('\\\\', '\\;', '\\,', '\\n'), $data); - return u2wi($data); - } - - /** - * Un-escapes a string - * - * @param string $data string to be un-escaped - * - * @access private - * @return string - */ - function unescape($data){ - $data = str_replace(array('\\\\', '\\;', '\\,', '\\n','\\N'),array('\\', ';', ',', "\n", "\n"),$data); - return $data; - } -} \ No newline at end of file diff --git a/sources/backend/zarafa/config.php b/sources/backend/zarafa/config.php deleted file mode 100644 index 625d86d..0000000 --- a/sources/backend/zarafa/config.php +++ /dev/null @@ -1,49 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -// ************************ -// BackendZarafa settings -// ************************ - -// Defines the server to which we want to connect -define('MAPI_SERVER', 'file:///var/run/zarafa'); diff --git a/sources/backend/zarafa/exporter.php b/sources/backend/zarafa/exporter.php deleted file mode 100644 index ee40092..0000000 --- a/sources/backend/zarafa/exporter.php +++ /dev/null @@ -1,297 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -/** - * This is our ICS exporter which requests the actual exporter from ICS and makes sure - * that the ImportProxies are used. - */ - -class ExportChangesICS implements IExportChanges{ - private $folderid; - private $store; - private $session; - private $restriction; - private $contentparameters; - private $flags; - private $exporterflags; - private $exporter; - - /** - * Constructor - * - * @param mapisession $session - * @param mapistore $store - * @param string (opt) - * - * @access public - * @throws StatusException - */ - public function ExportChangesICS($session, $store, $folderid = false) { - // Open a hierarchy or a contents exporter depending on whether a folderid was specified - $this->session = $session; - $this->folderid = $folderid; - $this->store = $store; - $this->restriction = false; - - try { - if($folderid) { - $entryid = mapi_msgstore_entryidfromsourcekey($store, $folderid); - } - else { - $storeprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID)); - $entryid = $storeprops[PR_IPM_SUBTREE_ENTRYID]; - } - - $folder = false; - if ($entryid) - $folder = mapi_msgstore_openentry($this->store, $entryid); - - // Get the actual ICS exporter - if($folderid) { - if ($folder) - $this->exporter = mapi_openproperty($folder, PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0 , 0); - else - $this->exporter = false; - } - else { - $this->exporter = mapi_openproperty($folder, PR_HIERARCHY_SYNCHRONIZER, IID_IExchangeExportChanges, 0 , 0); - } - } - catch (MAPIException $me) { - $this->exporter = false; - // We return the general error SYNC_FSSTATUS_CODEUNKNOWN (12) which is also SYNC_STATUS_FOLDERHIERARCHYCHANGED (12) - // if this happened while doing content sync, the mobile will try to resync the folderhierarchy - throw new StatusException(sprintf("ExportChangesICS('%s','%s','%s'): Error, unable to open folder: 0x%X", $session, $store, Utils::PrintAsString($folderid), mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN); - } - } - - /** - * Configures the exporter - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean - * @throws StatusException - */ - public function Config($state, $flags = 0) { - $this->exporterflags = 0; - $this->flags = $flags; - - // this should never happen - if ($this->exporter === false || is_array($state)) - throw new StatusException("ExportChangesICS->Config(): Error, exporter not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR); - - // change exporterflags if we are doing a ContentExport - if($this->folderid) { - $this->exporterflags |= SYNC_NORMAL | SYNC_READ_STATE; - - // Initial sync, we don't want deleted items. If the initial sync is chunked - // we check the change ID of the syncstate (0 at initial sync) - // On subsequent syncs, we do want to receive delete events. - if(strlen($state) == 0 || bin2hex(substr($state,4,4)) == "00000000") { - if (!($this->flags & BACKEND_DISCARD_DATA)) - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesICS->Config(): synching inital data"); - $this->exporterflags |= SYNC_NO_SOFT_DELETIONS | SYNC_NO_DELETIONS; - } - } - - if($this->flags & BACKEND_DISCARD_DATA) - $this->exporterflags |= SYNC_CATCHUP; - - // Put the state information in a stream that can be used by ICS - $stream = mapi_stream_create(); - if(strlen($state) == 0) - $state = hex2bin("0000000000000000"); - - if (!($this->flags & BACKEND_DISCARD_DATA)) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ExportChangesICS->Config() initialized with state: 0x%s", bin2hex($state))); - - mapi_stream_write($stream, $state); - $this->statestream = $stream; - } - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters){ - $filtertype = $contentparameters->GetFilterType(); - switch($contentparameters->GetContentClass()) { - case "Email": - $this->restriction = ($filtertype || !Utils::CheckMapiExtVersion('7')) ? MAPIUtils::GetEmailRestriction(Utils::GetCutOffDate($filtertype)) : false; - break; - case "Calendar": - $this->restriction = ($filtertype || !Utils::CheckMapiExtVersion('7')) ? MAPIUtils::GetCalendarRestriction($this->store, Utils::GetCutOffDate($filtertype)) : false; - break; - default: - case "Contacts": - case "Tasks": - $this->restriction = false; - break; - } - - $this->contentParameters = $contentparameters; - } - - - /** - * Sets the importer the exporter will sent it's changes to - * and initializes the Exporter - * - * @param object &$importer Implementation of IImportChanges - * - * @access public - * @return boolean - * @throws StatusException - */ - public function InitializeExporter(&$importer) { - // Because we're using ICS, we need to wrap the given importer to make it suitable to pass - // to ICS. We do this in two steps: first, wrap the importer with our own PHP importer class - // which removes all MAPI dependency, and then wrap that class with a C++ wrapper so we can - // pass it to ICS - - // this should never happen! - if($this->exporter === false || !isset($this->statestream) || !isset($this->flags) || !isset($this->exporterflags) || - ($this->folderid && !isset($this->contentParameters)) ) - throw new StatusException("ExportChangesICS->InitializeExporter(): Error, exporter or essential data not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR); - - // PHP wrapper - $phpwrapper = new PHPWrapper($this->session, $this->store, $importer, $this->folderid); - - // with a folderid we are going to get content - if($this->folderid) { - $phpwrapper->ConfigContentParameters($this->contentParameters); - - // ICS c++ wrapper - $mapiimporter = mapi_wrap_importcontentschanges($phpwrapper); - $includeprops = false; - } - else { - $mapiimporter = mapi_wrap_importhierarchychanges($phpwrapper); - $includeprops = array(PR_SOURCE_KEY, PR_DISPLAY_NAME); - } - - if (!$mapiimporter) - throw new StatusException(sprintf("ExportChangesICS->InitializeExporter(): Error, mapi_wrap_import_*_changes() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); - - $ret = mapi_exportchanges_config($this->exporter, $this->statestream, $this->exporterflags, $mapiimporter, $this->restriction, $includeprops, false, 1); - if(!$ret) - throw new StatusException(sprintf("ExportChangesICS->InitializeExporter(): Error, mapi_exportchanges_config() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); - - $changes = mapi_exportchanges_getchangecount($this->exporter); - if($changes || !($this->flags & BACKEND_DISCARD_DATA)) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ExportChangesICS->InitializeExporter() successfully. %d changes ready to sync for '%s'.", $changes, ($this->folderid)?bin2hex($this->folderid) : 'hierarchy')); - - return $ret; - } - - - /** - * Reads the current state from the Exporter - * - * @access public - * @return string - * @throws StatusException - */ - public function GetState() { - $error = false; - if(!isset($this->statestream) || $this->exporter === false) - $error = true; - - if($error === true || mapi_exportchanges_updatestate($this->exporter, $this->statestream) != true ) - throw new StatusException(sprintf("ExportChangesICS->GetState(): Error, state not available or unable to update: 0x%X", mapi_last_hresult()), (($this->folderid)?SYNC_STATUS_FOLDERHIERARCHYCHANGED:SYNC_FSSTATUS_CODEUNKNOWN), null, LOGLEVEL_WARN); - - mapi_stream_seek($this->statestream, 0, STREAM_SEEK_SET); - - $state = ""; - while(true) { - $data = mapi_stream_read($this->statestream, 4096); - if(strlen($data)) - $state .= $data; - else - break; - } - - return $state; - } - - /** - * Returns the amount of changes to be exported - * - * @access public - * @return int - */ - public function GetChangeCount() { - if ($this->exporter) - return mapi_exportchanges_getchangecount($this->exporter); - else - return 0; - } - - /** - * Synchronizes a change - * - * @access public - * @return array - */ - public function Synchronize() { - if ($this->exporter) { - return mapi_exportchanges_synchronize($this->exporter); - } - return false; - } -} diff --git a/sources/backend/zarafa/icalparser.php b/sources/backend/zarafa/icalparser.php deleted file mode 100644 index b0f9434..0000000 --- a/sources/backend/zarafa/icalparser.php +++ /dev/null @@ -1,199 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ICalParser{ - private $props; - - /** - * Constructor - * - * @param mapistore $store - * @param array &$props properties to be set - * - * @access public - */ - public function ICalParser(&$store, &$props){ - $this->props = $props; - } - - /** - * Function reads calendar part and puts mapi properties into an array. - * - * @param string $ical the ical data - * @param array &$mapiprops mapi properties - * - * @access public - */ - public function ExtractProps($ical, &$mapiprops) { - //mapping between partstat in ical and MAPI Meeting Response classes as well as icons - $aClassMap = array( - "ACCEPTED" => array("class" => "IPM.Schedule.Meeting.Resp.Pos", "icon" => 0x405), - "DECLINED" => array("class" => "IPM.Schedule.Meeting.Resp.Neg", "icon" => 0x406), - "TENTATIVE" => array("class" => "IPM.Schedule.Meeting.Resp.Tent", "icon" => 0x407), - "NEEDS-ACTION" => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //iphone - "REQ-PARTICIPANT" => array("class" => "IPM.Schedule.Meeting.Request", "icon" => 0x404), //nokia - ); - - $aical = preg_split("/[\n]/", $ical); - $elemcount = count($aical); - $i=0; - $nextline = $aical[0]; - - //last element is empty - while ($i < $elemcount - 1) { - $line = $nextline; - $nextline = $aical[$i+1]; - - //if a line starts with a space or a tab it belongs to the previous line - while (strlen($nextline) > 0 && ($nextline{0} == " " || $nextline{0} == "\t")) { - $line = rtrim($line) . substr($nextline, 1); - $nextline = $aical[++$i + 1]; - } - $line = rtrim($line); - - switch (strtoupper($line)) { - case "BEGIN:VCALENDAR": - case "BEGIN:VEVENT": - case "END:VEVENT": - case "END:VCALENDAR": - break; - default: - unset ($field, $data, $prop_pos, $property); - if (preg_match ("/([^:]+):(.*)/", $line, $line)){ - $field = $line[1]; - $data = $line[2]; - $property = $field; - $prop_pos = strpos($property,';'); - if ($prop_pos !== false) $property = substr($property, 0, $prop_pos); - $property = strtoupper($property); - - switch ($property) { - case 'DTSTART': - $data = $this->getTimestampFromStreamerDate($data); - $mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $mapiprops[$this->props["clipstart"]] = $mapiprops[PR_START_DATE] = $data; - break; - - case 'DTEND': - $data = $this->getTimestampFromStreamerDate($data); - $mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $mapiprops[$this->props["recurrenceend"]] = $mapiprops[PR_END_DATE] = $data; - break; - - case 'UID': - $mapiprops[$this->props["goidtag"]] = $mapiprops[$this->props["goid2tag"]] = Utils::GetOLUidFromICalUid($data); - break; - - case 'ATTENDEE': - $fields = explode(";", $field); - foreach ($fields as $field) { - $prop_pos = strpos($field, '='); - if ($prop_pos !== false) { - switch (substr($field, 0, $prop_pos)) { - case 'PARTSTAT' : $partstat = substr($field, $prop_pos+1); break; - case 'CN' : $cn = substr($field, $prop_pos+1); break; - case 'ROLE' : $role = substr($field, $prop_pos+1); break; - case 'RSVP' : $rsvp = substr($field, $prop_pos+1); break; - } - } - } - if (isset($partstat) && isset($aClassMap[$partstat]) && - - (!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) { - $mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$partstat]['class']; - $mapiprops[PR_ICON_INDEX] = $aClassMap[$partstat]['icon']; - } - // START ADDED dw2412 to support meeting requests on HTC Android Mail App - elseif (isset($role) && isset($aClassMap[$role]) && - (!isset($mapiprops[PR_MESSAGE_CLASS]) || $mapiprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request")) { - $mapiprops[PR_MESSAGE_CLASS] = $aClassMap[$role]['class']; - $mapiprops[PR_ICON_INDEX] = $aClassMap[$role]['icon']; - } - // END ADDED dw2412 to support meeting requests on HTC Android Mail App - if (!isset($cn)) $cn = ""; - $data = str_replace ("MAILTO:", "", $data); - $attendee[] = array ('name' => stripslashes($cn), 'email' => stripslashes($data)); - break; - - case 'ORGANIZER': - $field = str_replace("ORGANIZER;CN=", "", $field); - $data = str_replace ("MAILTO:", "", $data); - $organizer[] = array ('name' => stripslashes($field), 'email' => stripslashes($data)); - break; - - case 'LOCATION': - $data = str_replace("\\n", "
", $data); - $data = str_replace("\\t", " ", $data); - $data = str_replace("\\r", "
", $data); - $data = stripslashes($data); - $mapiprops[$this->props["tneflocation"]] = $mapiprops[$this->props["location"]] = $data; - break; - } - } - break; - } - $i++; - - } - $mapiprops[$this->props["usetnef"]] = true; - } - - /** - * Converts an YYYYMMDDTHHMMSSZ kind of string into an unixtimestamp - * - * @param string $data - * - * @access private - * @return long - */ - private function getTimestampFromStreamerDate ($data) { - $data = str_replace('Z', '', $data); - $data = str_replace('T', '', $data); - - preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})/', $data, $regs); - if ($regs[1] < 1970) { - $regs[1] = '1971'; - } - return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - } -} diff --git a/sources/backend/zarafa/importer.php b/sources/backend/zarafa/importer.php deleted file mode 100644 index 4ed209b..0000000 --- a/sources/backend/zarafa/importer.php +++ /dev/null @@ -1,696 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - - -/** - * This is our local importer. Tt receives data from the PDA, for contents and hierarchy changes. - * It must therefore receive the incoming data and convert it into MAPI objects, and then send - * them to the ICS importer to do the actual writing of the object. - * The creation of folders is fairly trivial, because folders that are created on - * the PDA are always e-mail folders. - */ - -class ImportChangesICS implements IImportChanges { - private $folderid; - private $store; - private $session; - private $flags; - private $statestream; - private $importer; - private $memChanges; - private $mapiprovider; - private $conflictsLoaded; - private $conflictsContentParameters; - private $conflictsState; - private $cutoffdate; - private $contentClass; - - /** - * Constructor - * - * @param mapisession $session - * @param mapistore $store - * @param string $folderid (opt) - * - * @access public - * @throws StatusException - */ - public function ImportChangesICS($session, $store, $folderid = false) { - $this->session = $session; - $this->store = $store; - $this->folderid = $folderid; - $this->conflictsLoaded = false; - $this->cutoffdate = false; - $this->contentClass = false; - - if ($folderid) { - $entryid = mapi_msgstore_entryidfromsourcekey($store, $folderid); - } - else { - $storeprops = mapi_getprops($store, array(PR_IPM_SUBTREE_ENTRYID)); - $entryid = $storeprops[PR_IPM_SUBTREE_ENTRYID]; - } - - $folder = false; - if ($entryid) - $folder = mapi_msgstore_openentry($store, $entryid); - - if(!$folder) { - $this->importer = false; - - // We throw an general error SYNC_FSSTATUS_CODEUNKNOWN (12) which is also SYNC_STATUS_FOLDERHIERARCHYCHANGED (12) - // if this happened while doing content sync, the mobile will try to resync the folderhierarchy - throw new StatusException(sprintf("ImportChangesICS('%s','%s','%s'): Error, unable to open folder: 0x%X", $session, $store, Utils::PrintAsString($folderid), mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN); - } - - $this->mapiprovider = new MAPIProvider($this->session, $this->store); - - if ($folderid) - $this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportContentsChanges, 0 , 0); - else - $this->importer = mapi_openproperty($folder, PR_COLLECTOR, IID_IExchangeImportHierarchyChanges, 0 , 0); - } - - /** - * Initializes the importer - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean - * @throws StatusException - */ - public function Config($state, $flags = 0) { - $this->flags = $flags; - - // this should never happen - if ($this->importer === false) - throw new StatusException("ImportChangesICS->Config(): Error, importer not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_ERROR); - - // Put the state information in a stream that can be used by ICS - $stream = mapi_stream_create(); - if(strlen($state) == 0) - $state = hex2bin("0000000000000000"); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->Config(): initializing importer with state: 0x%s", bin2hex($state))); - - mapi_stream_write($stream, $state); - $this->statestream = $stream; - - if ($this->folderid !== false) { - // possible conflicting messages will be cached here - $this->memChanges = new ChangesMemoryWrapper(); - $stat = mapi_importcontentschanges_config($this->importer, $stream, $flags); - } - else - $stat = mapi_importhierarchychanges_config($this->importer, $stream, $flags); - - if (!$stat) - throw new StatusException(sprintf("ImportChangesICS->Config(): Error, mapi_import_*_changes_config() failed: 0x%X", mapi_last_hresult()), SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); - return $stat; - } - - /** - * Configures additional parameters for content selection - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters) { - $filtertype = $contentparameters->GetFilterType(); - switch($contentparameters->GetContentClass()) { - case "Email": - $this->cutoffdate = ($filtertype) ? Utils::GetCutOffDate($filtertype) : false; - break; - case "Calendar": - $this->cutoffdate = ($filtertype) ? Utils::GetCutOffDate($filtertype) : false; - break; - default: - case "Contacts": - case "Tasks": - $this->cutoffdate = false; - break; - } - $this->contentClass = $contentparameters->GetContentClass(); - } - - /** - * Reads state from the Importer - * - * @access public - * @return string - * @throws StatusException - */ - public function GetState() { - $error = false; - if(!isset($this->statestream) || $this->importer === false) - $error = true; - - if ($error === false && $this->folderid !== false && function_exists("mapi_importcontentschanges_updatestate")) - if(mapi_importcontentschanges_updatestate($this->importer, $this->statestream) != true) - $error = true; - - if ($error == true) - throw new StatusException(sprintf("ImportChangesICS->GetState(): Error, state not available or unable to update: 0x%X", mapi_last_hresult()), (($this->folderid)?SYNC_STATUS_FOLDERHIERARCHYCHANGED:SYNC_FSSTATUS_CODEUNKNOWN), null, LOGLEVEL_WARN); - - mapi_stream_seek($this->statestream, 0, STREAM_SEEK_SET); - - $state = ""; - while(true) { - $data = mapi_stream_read($this->statestream, 4096); - if(strlen($data)) - $state .= $data; - else - break; - } - - return $state; - } - - /** - * Checks if a message is in the synchronization interval (window) - * if a filter (e.g. Sync items two weeks back) or limits this synchronization. - * These checks only apply to Emails and Appointments only, Contacts, Tasks and Notes do not have time restrictions. - * - * @param string $messageid the message id to be checked - * - * @access private - * @return boolean - */ - private function isMessageInSyncInterval($messageid) { - // if there is no restriciton we do not need to check - if ($this->cutoffdate === false) - return true; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): cut off date is: %s", $messageid, $this->cutoffdate)); - - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($messageid)); - if(!$entryid) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): Error, unable to resolve message id: 0x%X", $messageid, mapi_last_hresult())); - return false; - } - - $mapimessage = mapi_msgstore_openentry($this->store, $entryid); - if(!$mapimessage) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->isMessageInSyncInterval('%s'): Error, unable to open entry id: 0x%X", $messageid, mapi_last_hresult())); - return false; - } - - if ($this->contentClass == "Email") - return MAPIUtils::IsInEmailSyncInterval($this->store, $mapimessage, $this->cutoffdate); - elseif ($this->contentClass == "Calendar") - return MAPIUtils::IsInCalendarSyncInterval($this->store, $mapimessage, $this->cutoffdate); - - return true; - } - - /**---------------------------------------------------------------------------------------------------------- - * Methods for ContentsExporter - */ - - /** - * Loads objects which are expected to be exported with the state - * Before importing/saving the actual message from the mobile, a conflict detection should be done - * - * @param ContentParameters $contentparameters class of objects - * @param string $state - * - * @access public - * @return boolean - * @throws StatusException - */ - public function LoadConflicts($contentparameters, $state) { - if (!isset($this->session) || !isset($this->store) || !isset($this->folderid)) - throw new StatusException("ImportChangesICS->LoadConflicts(): Error, can not load changes for conflict detection. Session, store or folder information not available", SYNC_STATUS_SERVERERROR); - - // save data to load changes later if necessary - $this->conflictsLoaded = false; - $this->conflictsContentParameters = $contentparameters; - $this->conflictsState = $state; - - ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->LoadConflicts(): will be loaded later if necessary"); - return true; - } - - /** - * Potential conflicts are only loaded when really necessary, - * e.g. on ADD or MODIFY - * - * @access private - * @return - */ - private function lazyLoadConflicts() { - if (!isset($this->session) || !isset($this->store) || !isset($this->folderid) || - !isset($this->conflictsContentParameters) || $this->conflictsState === false) { - ZLog::Write(LOGLEVEL_WARN, "ImportChangesICS->lazyLoadConflicts(): can not load potential conflicting changes in lazymode for conflict detection. Missing information"); - return false; - } - - if (!$this->conflictsLoaded) { - ZLog::Write(LOGLEVEL_DEBUG, "ImportChangesICS->lazyLoadConflicts(): loading.."); - - // configure an exporter so we can detect conflicts - $exporter = new ExportChangesICS($this->session, $this->store, $this->folderid); - $exporter->Config($this->conflictsState); - $exporter->ConfigContentParameters($this->conflictsContentParameters); - $exporter->InitializeExporter($this->memChanges); - - // monitor how long it takes to export potential conflicts - // if this takes "too long" we cancel this operation! - $potConflicts = $exporter->GetChangeCount(); - $started = time(); - $exported = 0; - while(is_array($exporter->Synchronize())) { - $exported++; - - // stop if this takes more than 15 seconds and there are more than 5 changes still to be exported - // within 20 seconds this should be finished or it will not be performed - if ((time() - $started) > 15 && ($potConflicts - $exported) > 5 ) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ImportChangesICS->lazyLoadConflicts(): conflict detection cancelled as operation is too slow. In %d seconds only %d from %d changes were processed.",(time() - $started), $exported, $potConflicts)); - $this->conflictsLoaded = true; - return; - } - } - $this->conflictsLoaded = true; - } - } - - /** - * Imports a single message - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean/string - failure / id of message - * @throws StatusException - */ - public function ImportMessageChange($id, $message) { - $parentsourcekey = $this->folderid; - if($id) - $sourcekey = hex2bin($id); - - $flags = 0; - $props = array(); - $props[PR_PARENT_SOURCE_KEY] = $parentsourcekey; - - // set the PR_SOURCE_KEY if available or mark it as new message - if($id) { - $props[PR_SOURCE_KEY] = $sourcekey; - - // on editing an existing message, check if it is in the synchronization interval - if (!$this->isMessageInSyncInterval($id)) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Message is outside the sync interval. Data not saved.", $id, get_class($message)), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - // check for conflicts - $this->lazyLoadConflicts(); - if($this->memChanges->IsChanged($id)) { - if ($this->flags & SYNC_CONFLICT_OVERWRITE_PIM) { - // in these cases the status SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT should be returned, so the mobile client can inform the end user - throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Server overwrites PIM. User is informed.", $id, get_class($message)), SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT, null, LOGLEVEL_INFO); - return false; - } - else - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from Server will be dropped! PIM overwrites server.", $id, get_class($message))); - } - if($this->memChanges->IsDeleted($id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Object was deleted on server.", $id, get_class($message))); - return false; - } - } - else - $flags = SYNC_NEW_MESSAGE; - - if(mapi_importcontentschanges_importmessagechange($this->importer, $props, $flags, $mapimessage)) { - $this->mapiprovider->SetMessage($mapimessage, $message); - mapi_message_savechanges($mapimessage); - - if (mapi_last_hresult()) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error, mapi_message_savechanges() failed: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - $sourcekeyprops = mapi_getprops($mapimessage, array (PR_SOURCE_KEY)); - return bin2hex($sourcekeyprops[PR_SOURCE_KEY]); - } - else - throw new StatusException(sprintf("ImportChangesICS->ImportMessageChange('%s','%s'): Error updating object: 0x%X", $id, get_class($message), mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); - } - - /** - * Imports a deletion. This may conflict if the local object has been modified - * - * @param string $id - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageDeletion($id) { - // check if the message is in the current syncinterval - if (!$this->isMessageInSyncInterval($id)) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Message is outside the sync interval and so far not deleted.", $id), SYNC_STATUS_OBJECTNOTFOUND); - - // check for conflicts - $this->lazyLoadConflicts(); - if($this->memChanges->IsChanged($id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data from Server will be dropped! PIM deleted object.", $id)); - } - elseif($this->memChanges->IsDeleted($id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id)); - return true; - } - - // do a 'soft' delete so people can un-delete if necessary - if(mapi_importcontentschanges_importmessagedeletion($this->importer, 1, array(hex2bin($id)))) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageDeletion('%s'): Error updating object: 0x%X", $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); - - return true; - } - - /** - * Imports a change in 'read' flag - * This can never conflict - * - * @param string $id - * @param int $flags - read/unread - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageReadFlag($id, $flags) { - // check if the message is in the current syncinterval - if (!$this->isMessageInSyncInterval($id)) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageReadFlag('%s','%d'): Message is outside the sync interval. Flags not updated.", $id, $flags), SYNC_STATUS_OBJECTNOTFOUND); - - // check for conflicts - /* - * Checking for conflicts is correct at this point, but is a very expensive operation. - * If the message was deleted, only an error will be shown. - * - $this->lazyLoadConflicts(); - if($this->memChanges->IsDeleted($id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesICS->ImportMessageReadFlag('%s'): Conflict detected. Data is already deleted. Request will be ignored.", $id)); - return true; - } - */ - - $readstate = array ( "sourcekey" => hex2bin($id), "flags" => $flags); - - if(!mapi_importcontentschanges_importperuserreadstatechange($this->importer, array($readstate) )) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageReadFlag('%s','%d'): Error setting read state: 0x%X", $id, $flags, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); - - return true; - } - - /** - * Imports a move of a message. This occurs when a user moves an item to another folder - * - * Normally, we would implement this via the 'offical' importmessagemove() function on the ICS importer, - * but the Zarafa importer does not support this. Therefore we currently implement it via a standard mapi - * call. This causes a mirror 'add/delete' to be sent to the PDA at the next sync. - * Manfred, 2010-10-21. For some mobiles import was causing duplicate messages in the destination folder - * (Mantis #202). Therefore we will create a new message in the destination folder, copy properties - * of the source message to the new one and then delete the source message. - * - * @param string $id - * @param string $newfolder destination folder - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageMove($id, $newfolder) { - if (strtolower($newfolder) == strtolower(bin2hex($this->folderid)) ) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, source and destination are equal", $id, $newfolder), SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST); - - // Get the entryid of the message we're moving - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid, hex2bin($id)); - if(!$entryid) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source message id", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - //open the source message - $srcmessage = mapi_msgstore_openentry($this->store, $entryid); - if (!$srcmessage) { - $code = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; - // if we move to the trash and the source message is not found, we can also just tell the mobile that we successfully moved to avoid errors (ZP-624) - if ($newfolder == ZPush::GetBackend()->GetWasteBasket()) { - $code = SYNC_MOVEITEMSSTATUS_SUCCESS; - } - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source message: 0x%X", $id, $newfolder, mapi_last_hresult()), $code); - } - - // check if the source message is in the current syncinterval - if (!$this->isMessageInSyncInterval($id)) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Source message is outside the sync interval. Move not performed.", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - // get correct mapi store for the destination folder - $dststore = ZPush::GetBackend()->GetMAPIStoreForFolderId(ZPush::GetAdditionalSyncFolderStore($newfolder), $newfolder); - if ($dststore === false) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open store of destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); - - $dstentryid = mapi_msgstore_entryidfromsourcekey($dststore, hex2bin($newfolder)); - if(!$dstentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); - - $dstfolder = mapi_msgstore_openentry($dststore, $dstentryid); - if(!$dstfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open destination folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); - - $newmessage = mapi_folder_createmessage($dstfolder); - if (!$newmessage) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to create message in destination folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDDESTID); - - // Copy message - mapi_copyto($srcmessage, array(), array(), $newmessage); - if (mapi_last_hresult()) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, copy to destination message failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - $srcfolderentryid = mapi_msgstore_entryidfromsourcekey($this->store, $this->folderid); - if(!$srcfolderentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to resolve source folder", $id, $newfolder), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - $srcfolder = mapi_msgstore_openentry($this->store, $srcfolderentryid); - if (!$srcfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, unable to open source folder: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - // Save changes - mapi_savechanges($newmessage); - if (mapi_last_hresult()) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, mapi_savechanges() failed: 0x%X", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - // Delete the old message - if (!mapi_folder_deletemessages($srcfolder, array($entryid))) - throw new StatusException(sprintf("ImportChangesICS->ImportMessageMove('%s','%s'): Error, delete of source message failed: 0x%X. Possible duplicates.", $id, $newfolder, mapi_last_hresult()), SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED); - - $sourcekeyprops = mapi_getprops($newmessage, array (PR_SOURCE_KEY)); - if (isset($sourcekeyprops[PR_SOURCE_KEY]) && $sourcekeyprops[PR_SOURCE_KEY]) - return bin2hex($sourcekeyprops[PR_SOURCE_KEY]); - - return false; - } - - - /**---------------------------------------------------------------------------------------------------------- - * Methods for HierarchyExporter - */ - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return string id of the folder - * @throws StatusException - */ - public function ImportFolderChange($folder) { - $id = isset($folder->serverid)?$folder->serverid:false; - $parent = $folder->parentid; - $displayname = u2wi($folder->displayname); - $type = $folder->type; - - if (Utils::IsSystemFolder($type)) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, system folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER); - - // create a new folder if $id is not set - if (!$id) { - // the root folder is "0" - get IPM_SUBTREE - if ($parent == "0") { - $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID)); - if (isset($parentprops[PR_IPM_SUBTREE_ENTRYID])) - $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID]; - } - else - $parentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent)); - - if (!$parentfentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (no entry id)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND); - - $parentfolder = mapi_msgstore_openentry($this->store, $parentfentryid); - if (!$parentfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent folder (open entry)", Utils::PrintAsString(false), $folder->parentid, $displayname), SYNC_FSSTATUS_PARENTNOTFOUND); - - // mapi_folder_createfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION - $newfolder = mapi_folder_createfolder($parentfolder, $displayname, ""); - if (mapi_last_hresult()) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_folder_createfolder() failed: 0x%X", Utils::PrintAsString(false), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS); - - mapi_setprops($newfolder, array(PR_CONTAINER_CLASS => MAPIUtils::GetContainerClassFromFolderType($type))); - - $props = mapi_getprops($newfolder, array(PR_SOURCE_KEY)); - if (isset($props[PR_SOURCE_KEY])) { - $sourcekey = bin2hex($props[PR_SOURCE_KEY]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Created folder '%s' with id: '%s'", $displayname, $sourcekey)); - return $sourcekey; - } - else - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder created but PR_SOURCE_KEY not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - return false; - } - - // open folder for update - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id)); - if (!$entryid) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND); - - // check if this is a MAPI default folder - if ($this->mapiprovider->IsMAPIDefaultFolder($entryid)) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, MAPI default folder can not be created/modified", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname), SYNC_FSSTATUS_SYSTEMFOLDER); - - $mfolder = mapi_msgstore_openentry($this->store, $entryid); - if (!$mfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND); - - $props = mapi_getprops($mfolder, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_DISPLAY_NAME, PR_CONTAINER_CLASS)); - if (!isset($props[PR_SOURCE_KEY]) || !isset($props[PR_PARENT_SOURCE_KEY]) || !isset($props[PR_DISPLAY_NAME]) || !isset($props[PR_CONTAINER_CLASS])) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, folder data not available: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - - // get the real parent source key from mapi - if ($parent == "0") { - $parentprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID)); - $parentfentryid = $parentprops[PR_IPM_SUBTREE_ENTRYID]; - $mapifolder = mapi_msgstore_openentry($this->store, $parentfentryid); - - $rootfolderprops = mapi_getprops($mapifolder, array(PR_SOURCE_KEY)); - $parent = bin2hex($rootfolderprops[PR_SOURCE_KEY]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderChange(): resolved AS parent '0' to sourcekey '%s'", $parent)); - } - - // a changed parent id means that the folder should be moved - if (bin2hex($props[PR_PARENT_SOURCE_KEY]) !== $parent) { - $sourceparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, $props[PR_PARENT_SOURCE_KEY]); - if(!$sourceparentfentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND); - - $sourceparentfolder = mapi_msgstore_openentry($this->store, $sourceparentfentryid); - if(!$sourceparentfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open parent source folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_PARENTNOTFOUND); - - $destparentfentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($parent)); - if(!$sourceparentfentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (no entry id): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - - $destfolder = mapi_msgstore_openentry($this->store, $destparentfentryid); - if(!$destfolder) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to open destination folder (open entry): 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - - // mapi_folder_copyfolder() fails if a folder with this name already exists -> MAPI_E_COLLISION - if(! mapi_folder_copyfolder($sourceparentfolder, $entryid, $destfolder, $displayname, FOLDER_MOVE)) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, unable to move folder: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_FOLDEREXISTS); - - $folderProps = mapi_getprops($mfolder, array(PR_SOURCE_KEY)); - return $folderProps[PR_SOURCE_KEY]; - } - - // update the display name - $props = array(PR_DISPLAY_NAME => $displayname); - mapi_setprops($mfolder, $props); - mapi_savechanges($mfolder); - if (mapi_last_hresult()) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderChange('%s','%s','%s'): Error, mapi_savechanges() failed: 0x%X", Utils::PrintAsString($folder->serverid), $folder->parentid, $displayname, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - - ZLog::Write(LOGLEVEL_DEBUG, "Imported changes for folder: $id"); - return $id; - } - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent id is ignored in ICS - * - * @access public - * @return int SYNC_FOLDERHIERARCHY_STATUS - * @throws StatusException - */ - public function ImportFolderDeletion($id, $parent = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): importing folder deletetion", $id, $parent)); - - $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($id)); - if(!$folderentryid) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error, unable to resolve folder", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_FOLDERDOESNOTEXIST); - - // get the folder type from the MAPIProvider - $type = $this->mapiprovider->GetFolderType($folderentryid); - - if (Utils::IsSystemFolder($type) || $this->mapiprovider->IsMAPIDefaultFolder($folderentryid)) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting system/default folder", $id, $parent), SYNC_FSSTATUS_SYSTEMFOLDER); - - $ret = mapi_importhierarchychanges_importfolderdeletion ($this->importer, 0, array(PR_SOURCE_KEY => hex2bin($id))); - if (!$ret) - throw new StatusException(sprintf("ImportChangesICS->ImportFolderDeletion('%s','%s'): Error deleting folder: 0x%X", $id, $parent, mapi_last_hresult()), SYNC_FSSTATUS_SERVERERROR); - - return $ret; - } -} diff --git a/sources/backend/zarafa/listfolders.php b/sources/backend/zarafa/listfolders.php deleted file mode 100755 index f88dfac..0000000 --- a/sources/backend/zarafa/listfolders.php +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/php -. -* -* Consult LICENSE file for details -************************************************/ - -define("PHP_MAPI_PATH", "/usr/share/php/mapi/"); -define('MAPI_SERVER', 'file:///var/run/zarafa'); -define('SSLCERT_FILE', null); -define('SSLCERT_PASS', null); - -$supported_classes = array ( - "IPF.Note" => "SYNC_FOLDER_TYPE_USER_MAIL", - "IPF.Task" => "SYNC_FOLDER_TYPE_USER_TASK", - "IPF.Appointment" => "SYNC_FOLDER_TYPE_USER_APPOINTMENT", - "IPF.Contact" => "SYNC_FOLDER_TYPE_USER_CONTACT", - "IPF.StickyNote" => "SYNC_FOLDER_TYPE_USER_NOTE" -); - -main(); - -function main() { - listfolders_configure(); - listfolders_handle(); -} - -function listfolders_configure() { - - if (php_sapi_name() != "cli") { - printf("This script should not be called in a browser. Called from: %s\n", php_sapi_name()); - exit(1); - } - - if (!function_exists("getopt")) { - echo "PHP Function 'getopt()' not found. Please check your PHP version and settings.\n"; - exit(1); - } - - require(PHP_MAPI_PATH.'mapi.util.php'); - require(PHP_MAPI_PATH.'mapidefs.php'); - require(PHP_MAPI_PATH.'mapicode.php'); - require(PHP_MAPI_PATH.'mapitags.php'); - require(PHP_MAPI_PATH.'mapiguid.php'); -} - -function listfolders_handle() { - $shortoptions = "l:h:u:p:c:"; - $options = getopt($shortoptions); - - $mapi = MAPI_SERVER; - $sslcert_file = SSLCERT_FILE; - $sslcert_pass = SSLCERT_PASS; - $user = "SYSTEM"; - $pass = ""; - - if (isset($options['h'])) - $mapi = $options['h']; - - // accept a remote user - if (isset($options['u']) && isset($options['p'])) { - $user = $options['u']; - $pass = $options['p']; - } - // accept a certificate and passwort for login - else if (isset($options['c']) && isset($options['p'])) { - $sslcert_file = $options['c']; - $sslcert_pass = $options['p']; - } - - $zarafaAdmin = listfolders_zarafa_admin_setup($mapi, $user, $pass, $sslcert_file, $sslcert_pass); - if (isset($zarafaAdmin['adminStore']) && isset($options['l'])) { - listfolders_getlist($zarafaAdmin['adminStore'], $zarafaAdmin['session'], trim($options['l'])); - } - else { - echo "Usage:\nlistfolders.php [actions] [options]\n\nActions: [-l username]\n\t-l username\tlist folders of user, for public folder use 'SYSTEM'\n\nGlobal options: [-h path] [[-u remoteuser] [-p password]] [[-c certificate_path] [-p password]]\n\t-h path\t\tconnect through , e.g. file:///var/run/socket or https://10.0.0.1:237/zarafa\n\t-u remoteuser\tlogin as authenticated administration user\n\t-c certificate\tlogin with a ssl certificate located in this location, e.g. /etc/zarafa/ssl/client.pem\n\t-p password\tpassword of the remoteuser or certificate\n\n"; - } -} - -function listfolders_zarafa_admin_setup ($mapi, $user, $pass, $sslcert_file, $sslcert_pass) { - $session = @mapi_logon_zarafa($user, $pass, $mapi, $sslcert_file, $sslcert_pass); - - if (!$session) { - echo "User '$user' could not login. The script will exit. Errorcode: 0x". sprintf("%x", mapi_last_hresult()) . "\n"; - exit(1); - } - - $stores = @mapi_getmsgstorestable($session); - $storeslist = @mapi_table_queryallrows($stores); - $adminStore = @mapi_openmsgstore($session, $storeslist[0][PR_ENTRYID]); - - $zarafauserinfo = @mapi_zarafa_getuser_by_name($adminStore, $user); - $admin = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'])?true:false; - - if (!$stores || !$storeslist || !$adminStore || !$admin) { - echo "There was error trying to log in as admin or retrieving admin info. The script will exit.\n"; - exit(1); - } - - return array("session" => $session, "adminStore" => $adminStore); -} - - -function listfolders_getlist ($adminStore, $session, $user) { - global $supported_classes; - - if (strtoupper($user) == 'SYSTEM') { - // Find the public store store - $storestables = @mapi_getmsgstorestable($session); - $result = @mapi_last_hresult(); - - if ($result == NOERROR){ - $rows = @mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_MDB_PROVIDER)); - - foreach($rows as $row) { - if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { - if (!isset($row[PR_ENTRYID])) { - echo "Public folder are not available.\nIf this is a multi-tenancy system, use -u and -p and login with an admin user of the company.\nThe script will exit.\n"; - exit (1); - } - $entryid = $row[PR_ENTRYID]; - break; - } - } - } - } - else - $entryid = @mapi_msgstore_createentryid($adminStore, $user); - - $userStore = @mapi_openmsgstore($session, $entryid); - $hresult = mapi_last_hresult(); - - // Cache the store for later use - if($hresult != NOERROR) { - echo "Could not open store for '$user'. The script will exit.\n"; - exit (1); - } - - $folder = @mapi_msgstore_openentry($userStore); - $h_table = @mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH); - $subfolders = @mapi_table_queryallrows($h_table, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_CONTAINER_CLASS, PR_SOURCE_KEY)); - - echo "Available folders in store '$user':\n" . str_repeat("-", 50) . "\n"; - foreach($subfolders as $folder) { - if (isset($folder[PR_CONTAINER_CLASS]) && array_key_exists($folder[PR_CONTAINER_CLASS], $supported_classes)) { - echo "Folder name:\t". $folder[PR_DISPLAY_NAME] . "\n"; - echo "Folder ID:\t". bin2hex($folder[PR_SOURCE_KEY]) . "\n"; - echo "Type:\t\t". $supported_classes[$folder[PR_CONTAINER_CLASS]] . "\n"; - echo "\n"; - } - } -} diff --git a/sources/backend/zarafa/mapi/class.baseexception.php b/sources/backend/zarafa/mapi/class.baseexception.php deleted file mode 100644 index 77f9406..0000000 --- a/sources/backend/zarafa/mapi/class.baseexception.php +++ /dev/null @@ -1,225 +0,0 @@ -. - * - */ - - -/** - * Defines a base exception class for all custom exceptions, so every exceptions that - * is thrown/caught by this application should extend this base class and make use of it. - * it removes some peculiarities between different versions of PHP and exception handling. - * - * Some basic function of Exception class - * getMessage()- message of exception - * getCode() - code of exception - * getFile() - source filename - * getLine() - source line - * getTrace() - n array of the backtrace() - * getTraceAsString() - formated string of trace - */ -class BaseException extends Exception -{ - /** - * Reference of previous exception, only used for PHP < 5.3 - * can't use $previous here as its a private variable of parent class - */ - private $_previous = null; - - /** - * Base name of the file, so we don't have to use static path of the file - */ - private $baseFile = null; - - /** - * Flag to check if exception is already handled or not - */ - public $isHandled = false; - - /** - * The exception message to show at client side. - */ - public $displayMessage = null; - - /** - * Construct the exception - * - * @param string $errorMessage - * @param int $code - * @param Exception $previous - * @param string $displayMessage - * @return void - */ - public function __construct($errorMessage, $code = 0, Exception $previous = null, $displayMessage = null) { - // assign display message - $this->displayMessage = $displayMessage; - - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - parent::__construct($errorMessage, (int) $code); - - // set previous exception - if(!is_null($previous)) { - $this->_previous = $previous; - } - } else { - parent::__construct($errorMessage, (int) $code, $previous); - } - } - - /** - * Overloading of final methods to get rid of incompatibilities between different PHP versions. - * - * @param string $method - * @param array $args - * @return mixed - */ - public function __call($method, array $args) - { - if ('getprevious' == strtolower($method)) { - return $this->_getPrevious(); - } - - return null; - } - - /** - * @return string returns file name and line number combined where exception occured. - */ - public function getFileLine() - { - return $this->getBaseFile() . ':' . $this->getLine(); - } - - /** - * @return string returns message that should be sent to client to display - */ - public function getDisplayMessage() - { - if(!is_null($this->displayMessage)) { - return $this->displayMessage; - } - - return $this->getMessage(); - } - - /** - * Function sets display message of an exception that will be sent to the client side - * to show it to user. - * @param string $message display message. - */ - public function setDisplayMessage($message) - { - $this->displayMessage = $message; - } - - /** - * Function sets a flag in exception class to indicate that exception is already handled - * so if it is caught again in the top level of function stack then we have to silently - * ignore it. - */ - public function setHandled() - { - $this->isHandled = true; - } - - /** - * @return string returns base path of the file where exception occured. - */ - public function getBaseFile() - { - if(is_null($this->baseFile)) { - $this->baseFile = basename(parent::getFile()); - } - - return $this->baseFile; - } - - /** - * Function will check for PHP version if it is greater than 5.3 then we can use its default implementation - * otherwise we have to use our own implementation of chanining functionality. - * - * @return Exception returns previous exception - */ - public function _getPrevious() - { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - return $this->_previous; - } else { - return parent::getPrevious(); - } - } - - /** - * String representation of the exception, also handles previous exception. - * - * @return string - */ - public function __toString() - { - if (version_compare(PHP_VERSION, '5.3.0', '<')) { - if (($e = $this->getPrevious()) !== null) { - return $e->__toString() - . "\n\nNext " - . parent::__toString(); - } - } - - return parent::__toString(); - } - - /** - * Name of the class of exception. - * - * @return string - */ - public function getName() - { - return get_class($this); - } - - // @TODO getTrace and getTraceAsString -} diff --git a/sources/backend/zarafa/mapi/class.baserecurrence.php b/sources/backend/zarafa/mapi/class.baserecurrence.php deleted file mode 100644 index 0cac9c7..0000000 --- a/sources/backend/zarafa/mapi/class.baserecurrence.php +++ /dev/null @@ -1,1944 +0,0 @@ -. - * - */ - - /** - * BaseRecurrence - * this class is superclass for recurrence for appointments and tasks. This class provides all - * basic features of recurrence. - */ - class BaseRecurrence - { - /** - * @var object Mapi Message Store (may be null if readonly) - */ - var $store; - - /** - * @var object Mapi Message (may be null if readonly) - */ - var $message; - - /** - * @var array Message Properties - */ - var $messageprops; - - /** - * @var array list of property tags - */ - var $proptags; - - /** - * @var recurrence data of this calendar item - */ - var $recur; - - /** - * @var Timezone data of this calendar item - */ - var $tz; - - /** - * Constructor - * @param resource $store MAPI Message Store Object - * @param resource $message the MAPI (appointment) message - * @param array $properties the list of MAPI properties the message has. - */ - function BaseRecurrence($store, $message) - { - $this->store = $store; - - if(is_array($message)) { - $this->messageprops = $message; - } else { - $this->message = $message; - $this->messageprops = mapi_getprops($this->message, $this->proptags); - } - - if(isset($this->messageprops[$this->proptags["recurring_data"]])) { - // There is a possibility that recurr blob can be more than 255 bytes so get full blob through stream interface - if (strlen($this->messageprops[$this->proptags["recurring_data"]]) >= 255) { - $this->getFullRecurrenceBlob(); - } - - $this->recur = $this->parseRecurrence($this->messageprops[$this->proptags["recurring_data"]]); - } - if(isset($this->proptags["timezone_data"]) && isset($this->messageprops[$this->proptags["timezone_data"]])) { - $this->tz = $this->parseTimezone($this->messageprops[$this->proptags["timezone_data"]]); - } - } - - function getRecurrence() - { - return $this->recur; - } - - function getFullRecurrenceBlob() - { - $message = mapi_msgstore_openentry($this->store, $this->messageprops[PR_ENTRYID]); - - $recurrBlob = ''; - $stream = mapi_openproperty($message, $this->proptags["recurring_data"], IID_IStream, 0, 0); - $stat = mapi_stream_stat($stream); - - for ($i = 0; $i < $stat['cb']; $i += 1024) { - $recurrBlob .= mapi_stream_read($stream, 1024); - } - - if (!empty($recurrBlob)) { - $this->messageprops[$this->proptags["recurring_data"]] = $recurrBlob; - } - } - - /** - * Function for parsing the Recurrence value of a Calendar item. - * - * Retrieve it from Named Property 0x8216 as a PT_BINARY and pass the - * data to this function - * - * Returns a structure containing the data: - * - * type - type of recurrence: day=10, week=11, month=12, year=13 - * subtype - type of day recurrence: 2=monthday (ie 21st day of month), 3=nday'th weekdays (ie. 2nd Tuesday and Wednesday) - * start - unix timestamp of first occurrence - * end - unix timestamp of last occurrence (up to and including), so when start == end -> occurrences = 1 - * numoccur - occurrences (may be very large when there is no end data) - * - * then, for each type: - * - * Daily: - * everyn - every [everyn] days in minutes - * regen - regenerating event (like tasks) - * - * Weekly: - * everyn - every [everyn] weeks in weeks - * regen - regenerating event (like tasks) - * weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc) - * - * Monthly: - * everyn - every [everyn] months - * regen - regenerating event (like tasks) - * - * subtype 2: - * monthday - on day [monthday] of the month - * - * subtype 3: - * weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc) - * nday - on [nday]'th [weekdays] of the month - * - * Yearly: - * everyn - every [everyn] months (12, 24, 36, ...) - * month - in month [month] (although the month is encoded in minutes since the startning of the year ........) - * regen - regenerating event (like tasks) - * - * subtype 2: - * monthday - on day [monthday] of the month - * - * subtype 3: - * weekdays - bitmask of week days, where each bit is one weekday (weekdays & 1 = Sunday, weekdays & 2 = Monday, etc) - * nday - on [nday]'th [weekdays] of the month [month] - * @param string $rdata Binary string - * @return array recurrence data. - */ - function parseRecurrence($rdata) - { - if (strlen($rdata) < 10) { - return; - } - - $ret["changed_occurences"] = array(); - $ret["deleted_occurences"] = array(); - - $data = unpack("Vconst1/Crtype/Cconst2/Vrtype2", $rdata); - - $ret["type"] = $data["rtype"]; - $ret["subtype"] = $data["rtype2"]; - $rdata = substr($rdata, 10); - - switch ($data["rtype"]) - { - case 0x0a: - // Daily - if (strlen($rdata) < 12) { - return $ret; - } - - $data = unpack("Vunknown/Veveryn/Vregen", $rdata); - $ret["everyn"] = $data["everyn"]; - $ret["regen"] = $data["regen"]; - - switch($ret["subtype"]) - { - case 0: - $rdata = substr($rdata, 12); - break; - case 1: - $rdata = substr($rdata, 16); - break; - } - - break; - - case 0x0b: - // Weekly - if (strlen($rdata) < 16) { - return $ret; - } - - $data = unpack("Vconst1/Veveryn/Vregen", $rdata); - $rdata = substr($rdata, 12); - - $ret["everyn"] = $data["everyn"]; - $ret["regen"] = $data["regen"]; - $ret["weekdays"] = 0; - - if ($data["regen"] == 0) { - $data = unpack("Vweekdays", $rdata); - $rdata = substr($rdata, 4); - - $ret["weekdays"] = $data["weekdays"]; - } - break; - - case 0x0c: - // Monthly - if (strlen($rdata) < 16) { - return $ret; - } - - $data = unpack("Vconst1/Veveryn/Vregen/Vmonthday", $rdata); - - $ret["everyn"] = $data["everyn"]; - $ret["regen"] = $data["regen"]; - - if ($ret["subtype"] == 3) { - $ret["weekdays"] = $data["monthday"]; - } else { - $ret["monthday"] = $data["monthday"]; - } - - $rdata = substr($rdata, 16); - - if ($ret["subtype"] == 3) { - $data = unpack("Vnday", $rdata); - $ret["nday"] = $data["nday"]; - $rdata = substr($rdata, 4); - } - break; - - case 0x0d: - // Yearly - if (strlen($rdata) < 16) - return $ret; - - $data = unpack("Vmonth/Veveryn/Vregen/Vmonthday", $rdata); - - $ret["month"] = $data["month"]; - $ret["everyn"] = $data["everyn"]; - $ret["regen"] = $data["regen"]; - - if ($ret["subtype"] == 3) { - $ret["weekdays"] = $data["monthday"]; - } else { - $ret["monthday"] = $data["monthday"]; - } - - $rdata = substr($rdata, 16); - - if ($ret["subtype"] == 3) { - $data = unpack("Vnday", $rdata); - $ret["nday"] = $data["nday"]; - $rdata = substr($rdata, 4); - } - break; - } - - if (strlen($rdata) < 16) { - return $ret; - } - - $data = unpack("Cterm/C3const1/Vnumoccur/Vconst2/Vnumexcept", $rdata); - - $rdata = substr($rdata, 16); - - $ret["term"] = $data["term"]; - $ret["numoccur"] = $data["numoccur"]; - $ret["numexcept"] = $data["numexcept"]; - - // exc_base_dates are *all* the base dates that have been either deleted or modified - $exc_base_dates = array(); - for($i = 0; $i < $ret["numexcept"]; $i++) - { - if (strlen($rdata) < 4) { - // We shouldn't arrive here, because that implies - // numexcept does not match the amount of data - // which is available for the exceptions. - return $ret; - } - $data = unpack("Vbasedate", $rdata); - $rdata = substr($rdata, 4); - $exc_base_dates[] = $this->recurDataToUnixData($data["basedate"]); - } - - if (strlen($rdata) < 4) { - return $ret; - } - - $data = unpack("Vnumexceptmod", $rdata); - $rdata = substr($rdata, 4); - - $ret["numexceptmod"] = $data["numexceptmod"]; - - // exc_changed are the base dates of *modified* occurrences. exactly what is modified - // is in the attachments *and* in the data further down this function. - $exc_changed = array(); - for($i = 0; $i < $ret["numexceptmod"]; $i++) - { - if (strlen($rdata) < 4) { - // We shouldn't arrive here, because that implies - // numexceptmod does not match the amount of data - // which is available for the exceptions. - return $ret; - } - $data = unpack("Vstartdate", $rdata); - $rdata = substr($rdata, 4); - $exc_changed[] = $this->recurDataToUnixData($data["startdate"]); - } - - if (strlen($rdata) < 8) { - return $ret; - } - - $data = unpack("Vstart/Vend", $rdata); - $rdata = substr($rdata, 8); - - $ret["start"] = $this->recurDataToUnixData($data["start"]); - $ret["end"] = $this->recurDataToUnixData($data["end"]); - - // this is where task recurrence stop - if (strlen($rdata) < 16) { - return $ret; - } - - $data = unpack("Vreaderversion/Vwriterversion/Vstartmin/Vendmin", $rdata); - $rdata = substr($rdata, 16); - - $ret["startocc"] = $data["startmin"]; - $ret["endocc"] = $data["endmin"]; - $readerversion = $data["readerversion"]; - $writerversion = $data["writerversion"]; - - $data = unpack("vnumber", $rdata); - $rdata = substr($rdata, 2); - - $nexceptions = $data["number"]; - $exc_changed_details = array(); - - // Parse n modified exceptions - for($i=0;$i<$nexceptions;$i++) - { - $item = array(); - - // Get exception startdate, enddate and basedate (the date at which the occurrence would have started) - $data = unpack("Vstartdate/Venddate/Vbasedate", $rdata); - $rdata = substr($rdata, 12); - - // Convert recurtimestamp to unix timestamp - $startdate = $this->recurDataToUnixData($data["startdate"]); - $enddate = $this->recurDataToUnixData($data["enddate"]); - $basedate = $this->recurDataToUnixData($data["basedate"]); - - // Set the right properties - $item["basedate"] = $this->dayStartOf($basedate); - $item["start"] = $startdate; - $item["end"] = $enddate; - - $data = unpack("vbitmask", $rdata); - $rdata = substr($rdata, 2); - $item["bitmask"] = $data["bitmask"]; // save bitmask for extended exceptions - - // Bitmask to verify what properties are changed - $bitmask = $data["bitmask"]; - - // ARO_SUBJECT: 0x0001 - // Look for field: SubjectLength (2b), SubjectLength2 (2b) and Subject - if(($bitmask &(1 << 0))) { - $data = unpack("vnull_length/vlength", $rdata); - $rdata = substr($rdata, 4); - - $length = $data["length"]; - $item["subject"] = ""; // Normalized subject - for($j = 0; $j < $length && strlen($rdata); $j++) - { - $data = unpack("Cchar", $rdata); - $rdata = substr($rdata, 1); - - $item["subject"] .= chr($data["char"]); - } - } - - // ARO_MEETINGTYPE: 0x0002 - if(($bitmask &(1 << 1))) { - $rdata = substr($rdata, 4); - // Attendees modified: no data here (only in attachment) - } - - // ARO_REMINDERDELTA: 0x0004 - // Look for field: ReminderDelta (4b) - if(($bitmask &(1 << 2))) { - $data = unpack("Vremind_before", $rdata); - $rdata = substr($rdata, 4); - - $item["remind_before"] = $data["remind_before"]; - } - - // ARO_REMINDER: 0x0008 - // Look field: ReminderSet (4b) - if(($bitmask &(1 << 3))) { - $data = unpack("Vreminder_set", $rdata); - $rdata = substr($rdata, 4); - - $item["reminder_set"] = $data["reminder_set"]; - } - - // ARO_LOCATION: 0x0010 - // Look for fields: LocationLength (2b), LocationLength2 (2b) and Location - // Similar to ARO_SUBJECT above. - if(($bitmask &(1 << 4))) { - $data = unpack("vnull_length/vlength", $rdata); - $rdata = substr($rdata, 4); - - $item["location"] = ""; - - $length = $data["length"]; - $data = substr($rdata, 0, $length); - $rdata = substr($rdata, $length); - - $item["location"] .= $data; - } - - // ARO_BUSYSTATUS: 0x0020 - // Look for field: BusyStatus (4b) - if(($bitmask &(1 << 5))) { - $data = unpack("Vbusystatus", $rdata); - $rdata = substr($rdata, 4); - - $item["busystatus"] = $data["busystatus"]; - } - - // ARO_ATTACHMENT: 0x0040 - if(($bitmask &(1 << 6))) { - // no data: RESERVED - $rdata = substr($rdata, 4); - } - - // ARO_SUBTYPE: 0x0080 - // Look for field: SubType (4b). Determines whether it is an allday event. - if(($bitmask &(1 << 7))) { - $data = unpack("Vallday", $rdata); - $rdata = substr($rdata, 4); - - $item["alldayevent"] = $data["allday"]; - } - - // ARO_APPTCOLOR: 0x0100 - // Look for field: AppointmentColor (4b) - if(($bitmask &(1 << 8))) { - $data = unpack("Vlabel", $rdata); - $rdata = substr($rdata, 4); - - $item["label"] = $data["label"]; - } - - // ARO_EXCEPTIONAL_BODY: 0x0200 - if(($bitmask &(1 << 9))) { - // Notes or Attachments modified: no data here (only in attachment) - } - - array_push($exc_changed_details, $item); - } - - /** - * We now have $exc_changed, $exc_base_dates and $exc_changed_details - * We will ignore $exc_changed, as this information is available in $exc_changed_details - * also. If an item is in $exc_base_dates and NOT in $exc_changed_details, then the item - * has been deleted. - */ - - // Find deleted occurrences - $deleted_occurences = array(); - - foreach($exc_base_dates as $base_date) { - $found = false; - - foreach($exc_changed_details as $details) { - if($details["basedate"] == $base_date) { - $found = true; - break; - } - } - if(! $found) { - // item was not in exc_changed_details, so it must be deleted - $deleted_occurences[] = $base_date; - } - } - - $ret["deleted_occurences"] = $deleted_occurences; - $ret["changed_occurences"] = $exc_changed_details; - - // enough data for normal exception (no extended data) - if (strlen($rdata) < 16) { - return $ret; - } - - $data = unpack("Vreservedsize", $rdata); - $rdata = substr($rdata, 4 + $data["reservedsize"]); - - for($i=0;$i<$nexceptions;$i++) - { - // subject and location in ucs-2 to utf-8 - if ($writerversion >= 0x3009) { - $data = unpack("Vsize/Vvalue", $rdata); // size includes sizeof(value)==4 - $rdata = substr($rdata, 4 + $data["size"]); - } - - $data = unpack("Vreservedsize", $rdata); - $rdata = substr($rdata, 4 + $data["reservedsize"]); - - // ARO_SUBJECT(0x01) | ARO_LOCATION(0x10) - if ($exc_changed_details[$i]["bitmask"] & 0x11) { - $data = unpack("Vstart/Vend/Vorig", $rdata); - $rdata = substr($rdata, 4 * 3); - - $exc_changed_details[$i]["ex_start_datetime"] = $data["start"]; - $exc_changed_details[$i]["ex_end_datetime"] = $data["end"]; - $exc_changed_details[$i]["ex_orig_date"] = $data["orig"]; - } - - // ARO_SUBJECT - if ($exc_changed_details[$i]["bitmask"] & 0x01) { - // decode ucs2 string to utf-8 - $data = unpack("vlength", $rdata); - $rdata = substr($rdata, 2); - $length = $data["length"]; - $data = substr($rdata, 0, $length * 2); - $rdata = substr($rdata, $length * 2); - $subject = iconv("UCS-2LE", "UTF-8", $data); - // replace subject with unicode subject - $exc_changed_details[$i]["subject"] = $subject; - } - - // ARO_LOCATION - if ($exc_changed_details[$i]["bitmask"] & 0x10) { - // decode ucs2 string to utf-8 - $data = unpack("vlength", $rdata); - $rdata = substr($rdata, 2); - $length = $data["length"]; - $data = substr($rdata, 0, $length * 2); - $rdata = substr($rdata, $length * 2); - $location = iconv("UCS-2LE", "UTF-8", $data); - // replace subject with unicode subject - $exc_changed_details[$i]["location"] = $location; - } - - // ARO_SUBJECT(0x01) | ARO_LOCATION(0x10) - if ($exc_changed_details[$i]["bitmask"] & 0x11) { - $data = unpack("Vreservedsize", $rdata); - $rdata = substr($rdata, 4 + $data["reservedsize"]); - } - } - - // update with extended data - $ret["changed_occurences"] = $exc_changed_details; - - return $ret; - } - - /** - * Saves the recurrence data to the recurrence property - * @param array $properties the recurrence data. - * @return string binary string - */ - function saveRecurrence() - { - // Only save if a message was passed - if(!isset($this->message)) - return; - - // Abort if no recurrence was set - if(!isset($this->recur["type"]) && !isset($this->recur["subtype"])) { - return; - } - - if(!isset($this->recur["start"]) && !isset($this->recur["end"])) { - return; - } - - if(!isset($this->recur["startocc"]) && !isset($this->recur["endocc"])) { - return; - } - - $rdata = pack("CCCCCCV", 0x04, 0x30, 0x04, 0x30, (int) $this->recur["type"], 0x20, (int) $this->recur["subtype"]); - - $weekstart = 1; //monday - $forwardcount = 0; - $restocc = 0; - $dayofweek = (int) gmdate("w", (int) $this->recur["start"]); //0 (for Sunday) through 6 (for Saturday) - - $term = (int) $this->recur["type"]; - switch($term) - { - case 0x0A: - // Daily - if(!isset($this->recur["everyn"])) { - return; - } - - if($this->recur["subtype"] == 1) { - - // Daily every workday - $rdata .= pack("VVVV", (6 * 24 * 60), 1, 0, 0x3E); - } else { - // Daily every N days (everyN in minutes) - - $everyn = ((int) $this->recur["everyn"]) / 1440; - - // Calc first occ - $firstocc = $this->unixDataToRecurData($this->recur["start"]) % ((int) $this->recur["everyn"]); - - $rdata .= pack("VVV", $firstocc, (int) $this->recur["everyn"], $this->recur["regen"] ? 1 : 0); - } - break; - case 0x0B: - // Weekly - if(!isset($this->recur["everyn"])) { - return; - } - - if (!$this->recur["regen"] && !isset($this->recur["weekdays"])) { - return; - } - - // No need to calculate startdate if sliding flag was set. - if (!$this->recur['regen']) { - // Calculate start date of recurrence - - // Find the first day that matches one of the weekdays selected - $daycount = 0; - $dayskip = -1; - for($j = 0; $j < 7; $j++) { - if(((int) $this->recur["weekdays"]) & (1<<( ($dayofweek+$j)%7)) ) { - if($dayskip == -1) - $dayskip = $j; - - $daycount++; - } - } - - // $dayskip is the number of days to skip from the startdate until the first occurrence - // $daycount is the number of days per week that an occurrence occurs - - $weekskip = 0; - if(($dayofweek < $weekstart && $dayskip > 0) || ($dayofweek+$dayskip) > 6) - $weekskip = 1; - - // Check if the recurrence ends after a number of occurences, in that case we must calculate the - // remaining occurences based on the start of the recurrence. - if (((int) $this->recur["term"]) == 0x22) { - // $weekskip is the amount of weeks to skip from the startdate before the first occurence - // $forwardcount is the maximum number of week occurrences we can go ahead after the first occurrence that - // is still inside the recurrence. We subtract one to make sure that the last week is never forwarded over - // (eg when numoccur = 2, and daycount = 1) - $forwardcount = floor( (int) ($this->recur["numoccur"] -1 ) / $daycount); - - // $restocc is the number of occurrences left after $forwardcount whole weeks of occurrences, minus one - // for the occurrence on the first day - $restocc = ((int) $this->recur["numoccur"]) - ($forwardcount*$daycount) - 1; - - // $forwardcount is now the number of weeks we can go forward and still be inside the recurrence - $forwardcount *= (int) $this->recur["everyn"]; - } - - // The real start is start + dayskip + weekskip-1 (since dayskip will already bring us into the next week) - $this->recur["start"] = ((int) $this->recur["start"]) + ($dayskip * 24*60*60)+ ($weekskip *(((int) $this->recur["everyn"]) - 1) * 7 * 24*60*60); - } - - // Calc first occ - $firstocc = ($this->unixDataToRecurData($this->recur["start"]) ) % ( ((int) $this->recur["everyn"]) * 7 * 24 * 60); - - $firstocc -= (((int) gmdate("w", (int) $this->recur["start"])) - 1) * 24 * 60; - - if ($this->recur["regen"]) - $rdata .= pack("VVV", $firstocc, (int) $this->recur["everyn"], 1); - else - $rdata .= pack("VVVV", $firstocc, (int) $this->recur["everyn"], 0, (int) $this->recur["weekdays"]); - break; - case 0x0C: - // Monthly - case 0x0D: - // Yearly - if(!isset($this->recur["everyn"])) { - return; - } - if($term == 0x0D /*yearly*/ && !isset($this->recur["month"])) { - return; - } - - if($term == 0x0C /*monthly*/) { - $everyn = (int) $this->recur["everyn"]; - }else { - $everyn = $this->recur["regen"] ? ((int) $this->recur["everyn"]) * 12 : 12; - } - - // Get montday/month/year of original start - $curmonthday = gmdate("j", (int) $this->recur["start"] ); - $curyear = gmdate("Y", (int) $this->recur["start"] ); - $curmonth = gmdate("n", (int) $this->recur["start"] ); - - // Check if the recurrence ends after a number of occurences, in that case we must calculate the - // remaining occurences based on the start of the recurrence. - if (((int) $this->recur["term"]) == 0x22) { - // $forwardcount is the number of occurrences we can skip and still be inside the recurrence range (minus - // one to make sure there are always at least one occurrence left) - $forwardcount = ((((int) $this->recur["numoccur"])-1) * $everyn ); - } - - // Get month for yearly on D'th day of month M - if($term == 0x0D /*yearly*/) { - $selmonth = floor(((int) $this->recur["month"]) / (24 * 60 *29)) + 1; // 1=jan, 2=feb, eg - } - - switch((int) $this->recur["subtype"]) - { - // on D day of every M month - case 2: - if(!isset($this->recur["monthday"])) { - return; - } - // Recalc startdate - - // Set on the right begin day - - // Go the beginning of the month - $this->recur["start"] -= ($curmonthday-1) * 24*60*60; - // Go the the correct month day - $this->recur["start"] += (((int) $this->recur["monthday"])-1) * 24*60*60; - - // If the previous calculation gave us a start date *before* the original start date, then we need to skip to the next occurrence - if ( ($term == 0x0C /*monthly*/ && ((int) $this->recur["monthday"]) < $curmonthday) || - ($term == 0x0D /*yearly*/ &&( $selmonth < $curmonth || ($selmonth == $curmonth && ((int) $this->recur["monthday"]) < $curmonthday)) )) - { - if($term == 0x0D /*yearly*/) - $count = ($everyn - ($curmonth - $selmonth)); // Yearly, go to next occurrence in 'everyn' months minus difference in first occurence and original date - else - $count = $everyn; // Monthly, go to next occurrence in 'everyn' months - - // Forward by $count months. This is done by getting the number of days in that month and forwarding that many days - for($i=0; $i < $count; $i++) { - $this->recur["start"] += $this->getMonthInSeconds($curyear, $curmonth); - - if($curmonth == 12) { - $curyear++; - $curmonth = 0; - } - $curmonth++; - } - } - - // "start" is now pointing to the first occurrence, except that it will overshoot if the - // month in which it occurs has less days than specified as the day of the month. So 31st - // of each month will overshoot in february (29 days). We compensate for that by checking - // if the day of the month we got is wrong, and then back up to the last day of the previous - // month. - if(((int) $this->recur["monthday"]) >=28 && ((int) $this->recur["monthday"]) <= 31 && - gmdate("j", ((int) $this->recur["start"])) < ((int) $this->recur["monthday"])) - { - $this->recur["start"] -= gmdate("j", ((int) $this->recur["start"])) * 24 * 60 *60; - } - - // "start" is now the first occurrence - - if($term == 0x0C /*monthly*/) { - // Calc first occ - $monthIndex = ((((12%$everyn) * ((((int) gmdate("Y", $this->recur["start"])) - 1601)%$everyn)) % $everyn) + (((int) gmdate("n", $this->recur["start"])) - 1))%$everyn; - - $firstocc = 0; - for($i=0; $i < $monthIndex; $i++) { - $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), ($i%12)+1) / 60; - } - - $rdata .= pack("VVVV", $firstocc, $everyn, $this->recur["regen"], (int) $this->recur["monthday"]); - } else{ - // Calc first occ - $firstocc = 0; - $monthIndex = (int) gmdate("n", $this->recur["start"]); - for($i=1; $i < $monthIndex; $i++) { - $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), $i) / 60; - } - - $rdata .= pack("VVVV", $firstocc, $everyn, $this->recur["regen"], (int) $this->recur["monthday"]); - } - break; - - case 3: - // monthly: on Nth weekday of every M month - // yearly: on Nth weekday of M month - if(!isset($this->recur["weekdays"]) && !isset($this->recur["nday"])) { - return; - } - - $weekdays = (int) $this->recur["weekdays"]; - $nday = (int) $this->recur["nday"]; - - // Calc startdate - $monthbegindow = (int) $this->recur["start"]; - - if($nday == 5) { - // Set date on the last day of the last month - $monthbegindow += (gmdate("t", $monthbegindow ) - gmdate("j", $monthbegindow )) * 24 * 60 * 60; - }else { - // Set on the first day of the month - $monthbegindow -= ((gmdate("j", $monthbegindow )-1) * 24 * 60 * 60); - } - - if($term == 0x0D /*yearly*/) { - // Set on right month - if($selmonth < $curmonth) - $tmp = 12 - $curmonth + $selmonth; - else - $tmp = ($selmonth - $curmonth); - - for($i=0; $i < $tmp; $i++) { - $monthbegindow += $this->getMonthInSeconds($curyear, $curmonth); - - if($curmonth == 12) { - $curyear++; - $curmonth = 0; - } - $curmonth++; - } - - }else { - // Check or you exist in the right month - - for($i = 0; $i < 7; $i++) { - if($nday == 5 && (1<<( (gmdate("w", $monthbegindow)-$i)%7) ) & $weekdays) { - $day = gmdate("j", $monthbegindow) - $i; - break; - }else if($nday != 5 && (1<<( (gmdate("w", $monthbegindow )+$i)%7) ) & $weekdays) { - $day = (($nday-1)*7) + ($i+1); - break; - } - } - - // Goto the next X month - if(isset($day) && ($day < gmdate("j", (int) $this->recur["start"])) ) { - if($nday == 5) { - $monthbegindow += 24 * 60 * 60; - if($curmonth == 12) { - $curyear++; - $curmonth = 0; - } - $curmonth++; - } - - for($i=0; $i < $everyn; $i++) { - $monthbegindow += $this->getMonthInSeconds($curyear, $curmonth); - - if($curmonth == 12) { - $curyear++; - $curmonth = 0; - } - $curmonth++; - } - - if($nday == 5) { - $monthbegindow -= 24 * 60 * 60; - } - } - } - - //FIXME: weekstart? - - $day = 0; - // Set start on the right day - for($i = 0; $i < 7; $i++) { - if($nday == 5 && (1<<( (gmdate("w", $monthbegindow )-$i)%7) ) & $weekdays) { - $day = $i; - break; - }else if($nday != 5 && (1<<( (gmdate("w", $monthbegindow )+$i)%7) ) & $weekdays) { - $day = ($nday - 1) * 7 + ($i+1); - break; - } - } - if($nday == 5) - $monthbegindow -= $day * 24 * 60 *60; - else - $monthbegindow += ($day-1) * 24 * 60 *60; - - $firstocc = 0; - - if($term == 0x0C /*monthly*/) { - // Calc first occ - $monthIndex = ((((12%$everyn) * (((int) gmdate("Y", $this->recur["start"]) - 1601)%$everyn)) % $everyn) + (((int) gmdate("n", $this->recur["start"])) - 1))%$everyn; - - for($i=0; $i < $monthIndex; $i++) { - $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), ($i%12)+1) / 60; - } - - $rdata .= pack("VVVVV", $firstocc, $everyn, 0, $weekdays, $nday); - } else { - // Calc first occ - $monthIndex = (int) gmdate("n", $this->recur["start"]); - - for($i=1; $i < $monthIndex; $i++) { - $firstocc+= $this->getMonthInSeconds(1601 + floor($i/12), $i) / 60; - } - - $rdata .= pack("VVVVV", $firstocc, $everyn, 0, $weekdays, $nday); - } - break; - } - break; - - - - } - - if(!isset($this->recur["term"])) { - return; - } - - // Terminate - $term = (int) $this->recur["term"]; - $rdata .= pack("CCCC", $term, 0x20, 0x00, 0x00); - - switch($term) - { - // After the given enddate - case 0x21: - $rdata .= pack("V", 10); - break; - // After a number of times - case 0x22: - if(!isset($this->recur["numoccur"])) { - return; - } - - $rdata .= pack("V", (int) $this->recur["numoccur"]); - break; - // Never ends - case 0x23: - $rdata .= pack("V", 0); - break; - } - - // Strange little thing for the recurrence type "every workday" - if(((int) $this->recur["type"]) == 0x0B && ((int) $this->recur["subtype"]) == 1) { - $rdata .= pack("V", 1); - } else { // Other recurrences - $rdata .= pack("V", 0); - } - - // Exception data - - // Get all exceptions - $deleted_items = $this->recur["deleted_occurences"]; - $changed_items = $this->recur["changed_occurences"]; - - // Merge deleted and changed items into one list - $items = $deleted_items; - - foreach($changed_items as $changed_item) - array_push($items, $changed_item["basedate"]); - - sort($items); - - // Add the merged list in to the rdata - $rdata .= pack("V", count($items)); - foreach($items as $item) - $rdata .= pack("V", $this->unixDataToRecurData($item)); - - // Loop through the changed exceptions (not deleted) - $rdata .= pack("V", count($changed_items)); - $items = array(); - - foreach($changed_items as $changed_item) - { - $items[] = $this->dayStartOf($changed_item["start"]); - } - - sort($items); - - // Add the changed items list int the rdata - foreach($items as $item) - $rdata .= pack("V", $this->unixDataToRecurData($item)); - - // Set start date - $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["start"])); - - // Set enddate - switch($term) - { - // After the given enddate - case 0x21: - $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["end"])); - break; - // After a number of times - case 0x22: - // @todo: calculate enddate with intval($this->recur["startocc"]) + intval($this->recur["duration"]) > 24 hour - $occenddate = (int) $this->recur["start"]; - - switch((int) $this->recur["type"]) { - case 0x0A: //daily - - if($this->recur["subtype"] == 1) { - // Daily every workday - $restocc = (int) $this->recur["numoccur"]; - - // Get starting weekday - $nowtime = $this->gmtime($occenddate); - $j = $nowtime["tm_wday"]; - - while(1) - { - if(($j%7) > 0 && ($j%7)<6 ) { - $restocc--; - } - - $j++; - - if($restocc <= 0) - break; - - $occenddate += 24*60*60; - } - - } else { - // -1 because the first day already counts (from 1-1-1980 to 1-1-1980 is 1 occurrence) - $occenddate += (((int) $this->recur["everyn"]) * 60 * (((int) $this->recur["numoccur"]-1))); - } - break; - case 0x0B: //weekly - // Needed values - // $forwardcount - number of weeks we can skip forward - // $restocc - number of remaning occurrences after the week skip - - // Add the weeks till the last item - $occenddate+=($forwardcount*7*24*60*60); - - $dayofweek = gmdate("w", $occenddate); - - // Loop through the last occurrences until we have had them all - for($j = 1; $restocc>0; $j++) - { - // Jump to the next week (which may be N weeks away) when going over the week boundary - if((($dayofweek+$j)%7) == $weekstart) - $occenddate += (((int) $this->recur["everyn"])-1) * 7 * 24*60*60; - - // If this is a matching day, once less occurrence to process - if(((int) $this->recur["weekdays"]) & (1<<(($dayofweek+$j)%7)) ) { - $restocc--; - } - - // Next day - $occenddate += 24*60*60; - } - - break; - case 0x0C: //monthly - case 0x0D: //yearly - - $curyear = gmdate("Y", (int) $this->recur["start"] ); - $curmonth = gmdate("n", (int) $this->recur["start"] ); - // $forwardcount = months - - switch((int) $this->recur["subtype"]) - { - case 2: // on D day of every M month - while($forwardcount > 0) - { - $occenddate += $this->getMonthInSeconds($curyear, $curmonth); - - if($curmonth >=12) { - $curmonth = 1; - $curyear++; - } else { - $curmonth++; - } - $forwardcount--; - } - - // compensation between 28 and 31 - if(((int) $this->recur["monthday"]) >=28 && ((int) $this->recur["monthday"]) <= 31 && - gmdate("j", $occenddate) < ((int) $this->recur["monthday"])) - { - if(gmdate("j", $occenddate) < 28) - $occenddate -= gmdate("j", $occenddate) * 24 * 60 *60; - else - $occenddate += (gmdate("t", $occenddate) - gmdate("j", $occenddate)) * 24 * 60 *60; - } - - - break; - case 3: // on Nth weekday of every M month - $nday = (int) $this->recur["nday"]; //1 tot 5 - $weekdays = (int) $this->recur["weekdays"]; - - - while($forwardcount > 0) - { - $occenddate += $this->getMonthInSeconds($curyear, $curmonth); - if($curmonth >=12) { - $curmonth = 1; - $curyear++; - } else { - $curmonth++; - } - - $forwardcount--; - } - - if($nday == 5) { - // Set date on the last day of the last month - $occenddate += (gmdate("t", $occenddate ) - gmdate("j", $occenddate )) * 24 * 60 * 60; - }else { - // Set date on the first day of the last month - $occenddate -= (gmdate("j", $occenddate )-1) * 24 * 60 * 60; - } - - for($i = 0; $i < 7; $i++) { - if( $nday == 5 && (1<<( (gmdate("w", $occenddate)-$i)%7) ) & $weekdays) { - $occenddate -= $i * 24 * 60 * 60; - break; - }else if($nday != 5 && (1<<( (gmdate("w", $occenddate)+$i)%7) ) & $weekdays) { - $occenddate += ($i + (($nday-1) *7)) * 24 * 60 * 60; - break; - } - } - - break; //case 3: - } - - break; - - } - - if (defined("PHP_INT_MAX") && $occenddate > PHP_INT_MAX) - $occenddate = PHP_INT_MAX; - - $this->recur["end"] = $occenddate; - - $rdata .= pack("V", $this->unixDataToRecurData((int) $this->recur["end"]) ); - break; - // Never ends - case 0x23: - default: - $this->recur["end"] = 0x7fffffff; // max date -> 2038 - $rdata .= pack("V", 0x5AE980DF); - break; - } - - // UTC date - $utcstart = $this->toGMT($this->tz, (int) $this->recur["start"]); - $utcend = $this->toGMT($this->tz, (int) $this->recur["end"]); - - //utc date+time - $utcfirstoccstartdatetime = (isset($this->recur["startocc"])) ? $utcstart + (((int) $this->recur["startocc"])*60) : $utcstart; - $utcfirstoccenddatetime = (isset($this->recur["endocc"])) ? $utcstart + (((int) $this->recur["endocc"]) * 60) : $utcstart; - - // update reminder time - mapi_setprops($this->message, Array($this->proptags["reminder_time"] => $utcfirstoccstartdatetime )); - - // update first occurrence date - mapi_setprops($this->message, Array($this->proptags["startdate"] => $utcfirstoccstartdatetime )); - mapi_setprops($this->message, Array($this->proptags["duedate"] => $utcfirstoccenddatetime )); - mapi_setprops($this->message, Array($this->proptags["commonstart"] => $utcfirstoccstartdatetime )); - mapi_setprops($this->message, Array($this->proptags["commonend"] => $utcfirstoccenddatetime )); - - // Set Outlook properties, if it is an appointment - if (isset($this->recur["message_class"]) && $this->recur["message_class"] == "IPM.Appointment") { - // update real begin and real end date - mapi_setprops($this->message, Array($this->proptags["startdate_recurring"] => $utcstart)); - mapi_setprops($this->message, Array($this->proptags["enddate_recurring"] => $utcend)); - - // recurrencetype - // Strange enough is the property recurrencetype, (type-0x9) and not the CDO recurrencetype - mapi_setprops($this->message, Array($this->proptags["recurrencetype"] => ((int) $this->recur["type"]) - 0x9)); - - // set named prop 'side_effects' to 369, needed for Outlook to ask for single or total recurrence when deleting - mapi_setprops($this->message, Array($this->proptags["side_effects"] => 369)); - } else { - mapi_setprops($this->message, Array($this->proptags["side_effects"] => 3441)); - } - - // FlagDueBy is datetime of the first reminder occurrence. Outlook gives on this time a reminder popup dialog - // Any change of the recurrence (including changing and deleting exceptions) causes the flagdueby to be reset - // to the 'next' occurrence; this makes sure that deleting the next ocurrence will correctly set the reminder to - // the occurrence after that. The 'next' occurrence is defined as being the first occurrence that starts at moment X (server time) - // with the reminder flag set. - $reminderprops = mapi_getprops($this->message, array($this->proptags["reminder_minutes"]) ); - if(isset($reminderprops[$this->proptags["reminder_minutes"]]) ) { - $occ = false; - $occurrences = $this->getItems(time(), 0x7ff00000, 3, true); - - for($i = 0, $len = count($occurrences) ; $i < $len; $i++) { - // This will actually also give us appointments that have already started, but not yet ended. Since we want the next - // reminder that occurs after time(), we may have to skip the first few entries. We get 3 entries since that is the maximum - // number that would be needed (assuming reminder for item X cannot be before the previous occurrence starts). Worst case: - // time() is currently after start but before end of item, but reminder of next item has already passed (reminder for next item - // can be DURING the previous item, eg daily allday events). In that case, the first and second items must be skipped. - - if(($occurrences[$i][$this->proptags["startdate"]] - $reminderprops[$this->proptags["reminder_minutes"]] * 60) > time()) { - $occ = $occurrences[$i]; - break; - } - } - - if($occ) { - mapi_setprops($this->message, Array($this->proptags["flagdueby"] => $occ[$this->proptags["startdate"]] - ($reminderprops[$this->proptags["reminder_minutes"]] * 60) )); - } else { - // Last reminder passed, no reminders any more. - mapi_setprops($this->message, Array($this->proptags["reminder"] => false, $this->proptags["flagdueby"] => 0x7ff00000)); - } - } - - // Default data - // Second item (0x08) indicates the Outlook version (see documentation at the bottom of this file for more information) - $rdata .= pack("VCCCC", 0x00003006, 0x08, 0x30, 0x00, 0x00); - - if(isset($this->recur["startocc"]) && isset($this->recur["endocc"])) { - // Set start and endtime in minutes - $rdata .= pack("VV", (int) $this->recur["startocc"], (int) $this->recur["endocc"]); - } - - // Detailed exception data - - $changed_items = $this->recur["changed_occurences"]; - - $rdata .= pack("v", count($changed_items)); - - foreach($changed_items as $changed_item) - { - // Set start and end time of exception - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["start"])); - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["end"])); - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["basedate"])); - - //Bitmask - $bitmask = 0; - - // Check for changed strings - if(isset($changed_item["subject"])) { - $bitmask |= 1 << 0; - } - - if(isset($changed_item["remind_before"])) { - $bitmask |= 1 << 2; - } - - if(isset($changed_item["reminder_set"])) { - $bitmask |= 1 << 3; - } - - if(isset($changed_item["location"])) { - $bitmask |= 1 << 4; - } - - if(isset($changed_item["busystatus"])) { - $bitmask |= 1 << 5; - } - - if(isset($changed_item["alldayevent"])) { - $bitmask |= 1 << 7; - } - - if(isset($changed_item["label"])) { - $bitmask |= 1 << 8; - } - - $rdata .= pack("v", $bitmask); - - // Set "subject" - if(isset($changed_item["subject"])) { - // convert utf-8 to non-unicode blob string (us-ascii?) - $subject = iconv("UTF-8", "windows-1252//TRANSLIT", $changed_item["subject"]); - $length = strlen($subject); - $rdata .= pack("vv", $length + 1, $length); - $rdata .= pack("a".$length, $subject); - } - - if(isset($changed_item["remind_before"])) { - $rdata .= pack("V", $changed_item["remind_before"]); - } - - if(isset($changed_item["reminder_set"])) { - $rdata .= pack("V", $changed_item["reminder_set"]); - } - - if(isset($changed_item["location"])) { - $location = iconv("UTF-8", "windows-1252//TRANSLIT", $changed_item["location"]); - $length = strlen($location); - $rdata .= pack("vv", $length + 1, $length); - $rdata .= pack("a".$length, $location); - } - - if(isset($changed_item["busystatus"])) { - $rdata .= pack("V", $changed_item["busystatus"]); - } - - if(isset($changed_item["alldayevent"])) { - $rdata .= pack("V", $changed_item["alldayevent"]); - } - - if(isset($changed_item["label"])) { - $rdata .= pack("V", $changed_item["label"]); - } - } - - $rdata .= pack("V", 0); - - // write extended data - foreach($changed_items as $changed_item) - { - $rdata .= pack("V", 0); - if(isset($changed_item["subject"]) || isset($changed_item["location"])) { - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["start"])); - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["end"])); - $rdata .= pack("V", $this->unixDataToRecurData($changed_item["basedate"])); - } - - if(isset($changed_item["subject"])) { - $subject = iconv("UTF-8", "UCS-2LE", $changed_item["subject"]); - $length = iconv_strlen($subject, "UCS-2LE"); - $rdata .= pack("v", $length); - $rdata .= pack("a".$length*2, $subject); - } - - if(isset($changed_item["location"])) { - $location = iconv("UTF-8", "UCS-2LE", $changed_item["location"]); - $length = iconv_strlen($location, "UCS-2LE"); - $rdata .= pack("v", $length); - $rdata .= pack("a".$length*2, $location); - } - - if(isset($changed_item["subject"]) || isset($changed_item["location"])) { - $rdata .= pack("V", 0); - } - } - - $rdata .= pack("V", 0); - - // Set props - mapi_setprops($this->message, Array($this->proptags["recurring_data"] => $rdata, $this->proptags["recurring"] => true)); - if(isset($this->tz) && $this->tz){ - $timezone = "GMT"; - if ($this->tz["timezone"]!=0){ - // Create user readable timezone information - $timezone = sprintf("(GMT %s%02d:%02d)", (-$this->tz["timezone"]>0 ? "+" : "-"), - abs($this->tz["timezone"]/60), - abs($this->tz["timezone"]%60)); - } - mapi_setprops($this->message, Array($this->proptags["timezone_data"] => $this->getTimezoneData($this->tz), - $this->proptags["timezone"] => $timezone)); - } - } - - /** - * Function which converts a recurrence date timestamp to an unix date timestamp. - * @author Steve Hardy - * @param Int $rdate the date which will be converted - * @return Int the converted date - */ - function recurDataToUnixData($rdate) - { - return ($rdate - 194074560) * 60 ; - } - - /** - * Function which converts an unix date timestamp to recurrence date timestamp. - * @author Johnny Biemans - * @param Date $date the date which will be converted - * @return Int the converted date in minutes - */ - function unixDataToRecurData($date) - { - return ($date / 60) + 194074560; - } - - /** - * gmtime() doesn't exist in standard PHP, so we have to implement it ourselves - * @author Steve Hardy - */ - function GetTZOffset($ts) - { - $Offset = date("O", $ts); - - $Parity = $Offset < 0 ? -1 : 1; - $Offset = $Parity * $Offset; - $Offset = ($Offset - ($Offset % 100)) / 100 * 60 + $Offset % 100; - - return $Parity * $Offset; - } - - /** - * gmtime() doesn't exist in standard PHP, so we have to implement it ourselves - * @author Steve Hardy - * @param Date $time - * @return Date GMT Time - */ - function gmtime($time) - { - $TZOffset = $this->GetTZOffset($time); - - $t_time = $time - $TZOffset * 60; #Counter adjust for localtime() - $t_arr = localtime($t_time, 1); - - return $t_arr; - } - - function isLeapYear($year) { - return ( $year % 4 == 0 && ($year % 100 != 0 || $year % 400 == 0) ); - } - - function getMonthInSeconds($year, $month) - { - if( in_array($month, array(1,3,5,7,8,10,12) ) ) { - $day = 31; - } else if( in_array($month, array(4,6,9,11) ) ) { - $day = 30; - } else { - $day = 28; - if( $this->isLeapYear($year) == 1 ) - $day++; - } - return $day * 24 * 60 * 60; - } - - /** - * Function to get a date by Year Nr, Month Nr, Week Nr, Day Nr, and hour - * @param int $year - * @param int $month - * @param int $week - * @param int $day - * @param int $hour - * @return returns the timestamp of the given date, timezone-independant - */ - function getDateByYearMonthWeekDayHour($year, $month, $week, $day, $hour) - { - // get first day of month - $date = gmmktime(0,0,0,$month,0,$year + 1900); - - // get wday info - $gmdate = $this->gmtime($date); - - $date -= $gmdate["tm_wday"] * 24 * 60 * 60; // back up to start of week - - $date += $week * 7 * 24 * 60 * 60; // go to correct week nr - $date += $day * 24 * 60 * 60; - $date += $hour * 60 * 60; - - $gmdate = $this->gmtime($date); - - // if we are in the next month, then back up a week, because week '5' means - // 'last week of month' - - if($gmdate["tm_mon"]+1 != $month) - $date -= 7 * 24 * 60 * 60; - - return $date; - } - - /** - * getTimezone gives the timezone offset (in minutes) of the given - * local date/time according to the given TZ info - */ - function getTimezone($tz, $date) - { - // No timezone -> GMT (+0) - if(!isset($tz["timezone"])) - return 0; - - $dst = false; - $gmdate = $this->gmtime($date); - - $dststart = $this->getDateByYearMonthWeekDayHour($gmdate["tm_year"], $tz["dststartmonth"], $tz["dststartweek"], 0, $tz["dststarthour"]); - $dstend = $this->getDateByYearMonthWeekDayHour($gmdate["tm_year"], $tz["dstendmonth"], $tz["dstendweek"], 0, $tz["dstendhour"]); - - if($dststart <= $dstend) { - // Northern hemisphere, eg DST is during Mar-Oct - if($date > $dststart && $date < $dstend) { - $dst = true; - } - } else { - // Southern hemisphere, eg DST is during Oct-Mar - if($date < $dstend || $date > $dststart) { - $dst = true; - } - } - - if($dst) { - return $tz["timezone"] + $tz["timezonedst"]; - } else { - return $tz["timezone"]; - } - } - - /** - * getWeekNr() returns the week nr of the month (ie first week of february is 1) - */ - function getWeekNr($date) - { - $gmdate = gmtime($date); - $gmdate["tm_mday"] = 0; - return strftime("%W", $date) - strftime("%W", gmmktime($gmdate)) + 1; - } - - /** - * parseTimezone parses the timezone as specified in named property 0x8233 - * in Outlook calendar messages. Returns the timezone in minutes negative - * offset (GMT +2:00 -> -120) - */ - function parseTimezone($data) - { - if(strlen($data) < 48) - return; - - $tz = unpack("ltimezone/lunk/ltimezonedst/lunk/ldstendmonth/vdstendweek/vdstendhour/lunk/lunk/vunk/ldststartmonth/vdststartweek/vdststarthour/lunk/vunk", $data); - return $tz; - } - - function getTimezoneData($tz) - { - $data = pack("lllllvvllvlvvlv", $tz["timezone"], 0, $tz["timezonedst"], 0, $tz["dstendmonth"], $tz["dstendweek"], $tz["dstendhour"], 0, 0, 0, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststarthour"], 0 ,0); - - return $data; - } - - /** - * createTimezone creates the timezone as specified in the named property 0x8233 - * see also parseTimezone() - * $tz is an array with the timezone data - */ - function createTimezone($tz) - { - $data = pack("lxxxxlxxxxlvvxxxxxxxxxxlvvxxxxxx", - $tz["timezone"], - array_key_exists("timezonedst",$tz)?$tz["timezonedst"]:0, - array_key_exists("dstendmonth",$tz)?$tz["dstendmonth"]:0, - array_key_exists("dstendweek",$tz)?$tz["dstendweek"]:0, - array_key_exists("dstendhour",$tz)?$tz["dstendhour"]:0, - array_key_exists("dststartmonth",$tz)?$tz["dststartmonth"]:0, - array_key_exists("dststartweek",$tz)?$tz["dststartweek"]:0, - array_key_exists("dststarthour",$tz)?$tz["dststarthour"]:0 - ); - - return $data; - } - - /** - * toGMT returns a timestamp in GMT time for the time and timezone given - */ - function toGMT($tz, $date) { - if(!isset($tz['timezone'])) - return $date; - $offset = $this->getTimezone($tz, $date); - - return $date + $offset * 60; - } - - /** - * fromGMT returns a timestamp in the local timezone given from the GMT time given - */ - function fromGMT($tz, $date) { - $offset = $this->getTimezone($tz, $date); - - return $date - $offset * 60; - } - - /** - * Function to get timestamp of the beginning of the day of the timestamp given - * @param date $date - * @return date timestamp referring to same day but at 00:00:00 - */ - function dayStartOf($date) - { - $time1 = $this->gmtime($date); - - return gmmktime(0, 0, 0, $time1["tm_mon"] + 1, $time1["tm_mday"], $time1["tm_year"] + 1900); - } - - /** - * Function to get timestamp of the beginning of the month of the timestamp given - * @param date $date - * @return date Timestamp referring to same month but on the first day, and at 00:00:00 - */ - function monthStartOf($date) - { - $time1 = $this->gmtime($date); - - return gmmktime(0, 0, 0, $time1["tm_mon"] + 1, 1, $time1["tm_year"] + 1900); - } - - /** - * Function to get timestamp of the beginning of the year of the timestamp given - * @param date $date - * @return date Timestamp referring to the same year but on Jan 01, at 00:00:00 - */ - function yearStartOf($date) - { - $time1 = $this->gmtime($date); - - return gmmktime(0, 0, 0, 1, 1, $time1["tm_year"] + 1900); - } - - - /** - * Function which returns the items in a given interval. This included expansion of the recurrence and - * processing of exceptions (modified and deleted). - * - * @param string $entryid the entryid of the message - * @param array $props the properties of the message - * @param date $start start time of the interval (GMT) - * @param date $end end time of the interval (GMT) - */ - function getItems($start, $end, $limit = 0, $remindersonly = false) - { - $items = array(); - - if(isset($this->recur)) { - - // Optimization: remindersonly and default reminder is off; since only exceptions with reminder set will match, just look which - // exceptions are in range and have a reminder set - if($remindersonly && (!isset($this->messageprops[$this->proptags["reminder"]]) || $this->messageprops[$this->proptags["reminder"]] == false)) { - // Sort exceptions by start time - uasort($this->recur["changed_occurences"], array($this, "sortExceptionStart")); - - // Loop through all changed exceptions - foreach($this->recur["changed_occurences"] as $exception) { - // Check reminder set - if(!isset($exception["reminder"]) || $exception["reminder"] == false) - continue; - - // Convert to GMT - $occstart = $this->toGMT($this->tz, $exception["start"]); // seb changed $tz to $this->tz - $occend = $this->toGMT($this->tz, $exception["end"]); // seb changed $tz to $this->tz - - // Check range criterium - if($occstart > $end || $occend < $start) - continue; - - // OK, add to items. - array_push($items, $this->getExceptionProperties($exception)); - if($limit && (count($items) == $limit)) - break; - } - - uasort($items, array($this, "sortStarttime")); - - return $items; - } - - // From here on, the dates of the occurrences are calculated in local time, so the days we're looking - // at are calculated from the local time dates of $start and $end - - if ($this->recur['regen'] && isset($this->action['datecompleted'])) { - $daystart = $this->dayStartOf($this->action['datecompleted']); - } else { - $daystart = $this->dayStartOf($this->recur["start"]); // start on first day of occurrence - } - - // Calculate the last day on which we want to be looking at a recurrence; this is either the end of the view - // or the end of the recurrence, whichever comes first - if($end > $this->toGMT($this->tz, $this->recur["end"])) { - $rangeend = $this->toGMT($this->tz, $this->recur["end"]); - } else { - $rangeend = $end; - } - - $dayend = $this->dayStartOf($this->fromGMT($this->tz, $rangeend)); - - // Loop through the entire recurrence range of dates, and check for each occurrence whether it is in the view range. - - switch($this->recur["type"]) - { - case 10: - // Daily - if($this->recur["everyn"] <= 0) - $this->recur["everyn"] = 1440; - - if($this->recur["subtype"] == 0) { - // Every Nth day - for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += 60 * $this->recur["everyn"]) { - $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } else { - // Every workday - for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += 60 * 1440) - { - $nowtime = $this->gmtime($now); - if ($nowtime["tm_wday"] > 0 && $nowtime["tm_wday"] < 6) { // only add items in the given timespace - $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } - } - break; - case 11: - // Weekly - if($this->recur["everyn"] <= 0) - $this->recur["everyn"] = 1; - - // If sliding flag is set then move to 'n' weeks - if ($this->recur['regen']) $daystart += (60 * 60 * 24 * 7 * $this->recur["everyn"]); - - for($now = $daystart; $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += (60 * 60 * 24 * 7 * $this->recur["everyn"])) - { - if ($this->recur['regen']) { - $this->processOccurrenceItem($items, $start, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } else { - // Loop through the whole following week to the first occurrence of the week, add each day that is specified - for($wday = 0; $wday < 7; $wday++) - { - $daynow = $now + $wday * 60 * 60 * 24; - //checks weather the next coming day in recurring pattern is less than or equal to end day of the recurring item - if ($daynow <= $dayend){ - $nowtime = $this->gmtime($daynow); // Get the weekday of the current day - if(($this->recur["weekdays"] &(1 << $nowtime["tm_wday"]))) { // Selected ? - $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } - } - } - } - break; - case 12: - // Monthly - if($this->recur["everyn"] <= 0) - $this->recur["everyn"] = 1; - - // Loop through all months from start to end of occurrence, starting at beginning of first month - for($now = $this->monthStartOf($daystart); $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += $this->daysInMonth($now, $this->recur["everyn"]) * 24 * 60 * 60 ) - { - if(isset($this->recur["monthday"]) &&($this->recur['monthday'] != "undefined") && !$this->recur['regen']) { // Day M of every N months - $difference = 1; - if ($this->daysInMonth($now, $this->recur["everyn"]) < $this->recur["monthday"]) { - $difference = $this->recur["monthday"] - $this->daysInMonth($now, $this->recur["everyn"]) + 1; - } - $daynow = $now + (($this->recur["monthday"] - $difference) * 24 * 60 * 60); - //checks weather the next coming day in recurrence pattern is less than or equal to end day of the recurring item - if ($daynow <= $dayend){ - $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } - else if(isset($this->recur["nday"]) && isset($this->recur["weekdays"])) { // Nth [weekday] of every N months - // Sanitize input - if($this->recur["weekdays"] == 0) - $this->recur["weekdays"] = 1; - - // If nday is not set to the last day in the month - if ($this->recur["nday"] < 5) { - // keep the track of no. of time correct selection pattern(like 2nd weekday, 4th fiday, etc.)is matched - $ndaycounter = 0; - // Find matching weekday in this month - for($day = 0; $day < $this->daysInMonth($now, 1); $day++) - { - $daynow = $now + $day * 60 * 60 * 24; - $nowtime = $this->gmtime($daynow); // Get the weekday of the current day - - if($this->recur["weekdays"] & (1 << $nowtime["tm_wday"])) { // Selected ? - $ndaycounter ++; - } - // check the selected pattern is same as asked Nth weekday,If so set the firstday - if($this->recur["nday"] == $ndaycounter){ - $firstday = $day; - break; - } - } - // $firstday is the day of the month on which the asked pattern of nth weekday matches - $daynow = $now + $firstday * 60 * 60 * 24; - }else{ - // Find last day in the month ($now is the firstday of the month) - $NumDaysInMonth = $this->daysInMonth($now, 1); - $daynow = $now + (($NumDaysInMonth-1) * 24*60*60); - - $nowtime = $this->gmtime($daynow); - while (($this->recur["weekdays"] & (1 << $nowtime["tm_wday"]))==0){ - $daynow -= 86400; - $nowtime = $this->gmtime($daynow); - } - } - - /** - * checks weather the next coming day in recurrence pattern is less than or equal to end day of the * recurring item.Also check weather the coming day in recurrence pattern is greater than or equal to start * of recurring pattern, so that appointment that fall under the recurrence range are only displayed. - */ - if ($daynow <= $dayend && $daynow >= $daystart){ - $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz , $remindersonly); - } - } else if ($this->recur['regen']) { - $next_month_start = $now + ($this->daysInMonth($now, 1) * 24 * 60 * 60); - $now = $daystart +($this->daysInMonth($next_month_start, $this->recur['everyn']) * 24 * 60 * 60); - - if ($now <= $dayend) { - $this->processOccurrenceItem($items, $daystart, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } - } - break; - case 13: - // Yearly - if($this->recur["everyn"] <= 0) - $this->recur["everyn"] = 12; - - for($now = $this->yearStartOf($daystart); $now <= $dayend && ($limit == 0 || count($items) < $limit); $now += $this->daysInMonth($now, $this->recur["everyn"]) * 24 * 60 * 60 ) - { - if(isset($this->recur["monthday"]) && !$this->recur['regen']) { // same as monthly, but in a specific month - // recur["month"] is in minutes since the beginning of the year - $month = $this->monthOfYear($this->recur["month"]); // $month is now month of year [0..11] - $monthday = $this->recur["monthday"]; // $monthday is day of the month [1..31] - $monthstart = $now + $this->daysInMonth($now, $month) * 24 * 60 * 60; // $monthstart is the timestamp of the beginning of the month - if($monthday > $this->daysInMonth($monthstart, 1)) - $monthday = $this->daysInMonth($monthstart, 1); // Cap $monthday on month length (eg 28 feb instead of 29 feb) - $daynow = $monthstart + ($monthday-1) * 24 * 60 * 60; - $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - else if(isset($this->recur["nday"]) && isset($this->recur["weekdays"])) { // Nth [weekday] in month X of every N years - - // Go the correct month - $monthnow = $now + $this->daysInMonth($now, $this->monthOfYear($this->recur["month"])) * 24 * 60 * 60; - - // Find first matching weekday in this month - for($wday = 0; $wday < 7; $wday++) - { - $daynow = $monthnow + $wday * 60 * 60 * 24; - $nowtime = $this->gmtime($daynow); // Get the weekday of the current day - - if($this->recur["weekdays"] & (1 << $nowtime["tm_wday"])) { // Selected ? - $firstday = $wday; - break; - } - } - - // Same as above (monthly) - $daynow = $monthnow + ($firstday + ($this->recur["nday"]-1)*7) * 60 * 60 * 24; - - while($this->monthStartOf($daynow) != $this->monthStartOf($monthnow)) { - $daynow -= 7 * 60 * 60 * 24; - } - - $this->processOccurrenceItem($items, $start, $end, $daynow, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } else if ($this->recur['regen']) { - $year_starttime = $this->gmtime($now); - $is_next_leapyear = $this->isLeapYear($year_starttime['tm_year'] + 1900 + 1); // +1 next year - $now = $daystart + ($is_next_leapyear ? 31622400 /* Leap year in seconds */ : 31536000 /*year in seconds*/); - - if ($now <= $dayend) { - $this->processOccurrenceItem($items, $daystart, $end, $now, $this->recur["startocc"], $this->recur["endocc"], $this->tz, $remindersonly); - } - } - } - } - //to get all exception items - if (!empty($this->recur['changed_occurences'])) - $this->processExceptionItems($items, $start, $end); - } - - // sort items on starttime - usort($items, array($this, "sortStarttime")); - - // Return the MAPI-compatible list of items for this object - return $items; - } - - function sortStarttime($a, $b) - { - $aTime = $a[$this->proptags["startdate"]]; - $bTime = $b[$this->proptags["startdate"]]; - - return $aTime==$bTime?0:($aTime>$bTime?1:-1); - } - - /** - * daysInMonth - * - * Returns the number of days in the upcoming number of months. If you specify 1 month as - * $months it will give you the number of days in the month of $date. If you specify more it - * will also count the days in the upcomming months and add that to the number of days. So - * if you have a date in march and you specify $months as 2 it will return 61. - * @param Integer $date Specified date as timestamp from which you want to know the number - * of days in the month. - * @param Integer $months Number of months you want to know the number of days in. - * @returns Integer Number of days in the specified amount of months. - */ - function daysInMonth($date, $months) { - $days = 0; - - for($i=0;$i<$months;$i++) { - $days += date("t", $date + $days * 24 * 60 * 60); - } - - return $days; - } - - // Converts MAPI-style 'minutes' into the month of the year [0..11] - function monthOfYear($minutes) { - $d = gmmktime(0,0,0,1,1,2001); // The year 2001 was a non-leap year, and the minutes provided are always in non-leap-year-minutes - - $d += $minutes*60; - - $dtime = $this->gmtime($d); - - return $dtime["tm_mon"]; - } - - function sortExceptionStart($a, $b) - { - return $a["start"] == $b["start"] ? 0 : ($a["start"] > $b["start"] ? 1 : -1 ); - } - } diff --git a/sources/backend/zarafa/mapi/class.freebusypublish.php b/sources/backend/zarafa/mapi/class.freebusypublish.php deleted file mode 100644 index 36c8229..0000000 --- a/sources/backend/zarafa/mapi/class.freebusypublish.php +++ /dev/null @@ -1,396 +0,0 @@ -. - * - */ - - -include_once('backend/zarafa/mapi/class.recurrence.php'); - -class FreeBusyPublish { - - var $session; - var $calendar; - var $entryid; - var $starttime; - var $length; - var $store; - var $proptags; - - /** - * Constuctor - * - * @param mapi_session $session MAPI Session - * @param mapi_folder $calendar Calendar to publish - * @param string $entryid AddressBook Entry ID for the user we're publishing for - */ - - - function FreeBusyPublish($session, $store, $calendar, $entryid) - { - $properties["entryid"] = PR_ENTRYID; - $properties["parent_entryid"] = PR_PARENT_ENTRYID; - $properties["message_class"] = PR_MESSAGE_CLASS; - $properties["icon_index"] = PR_ICON_INDEX; - $properties["subject"] = PR_SUBJECT; - $properties["display_to"] = PR_DISPLAY_TO; - $properties["importance"] = PR_IMPORTANCE; - $properties["sensitivity"] = PR_SENSITIVITY; - $properties["startdate"] = "PT_SYSTIME:PSETID_Appointment:0x820d"; - $properties["duedate"] = "PT_SYSTIME:PSETID_Appointment:0x820e"; - $properties["recurring"] = "PT_BOOLEAN:PSETID_Appointment:0x8223"; - $properties["recurring_data"] = "PT_BINARY:PSETID_Appointment:0x8216"; - $properties["busystatus"] = "PT_LONG:PSETID_Appointment:0x8205"; - $properties["label"] = "PT_LONG:PSETID_Appointment:0x8214"; - $properties["alldayevent"] = "PT_BOOLEAN:PSETID_Appointment:0x8215"; - $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506"; - $properties["meeting"] = "PT_LONG:PSETID_Appointment:0x8217"; - $properties["startdate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8235"; - $properties["enddate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8236"; - $properties["location"] = "PT_STRING8:PSETID_Appointment:0x8208"; - $properties["duration"] = "PT_LONG:PSETID_Appointment:0x8213"; - $properties["responsestatus"] = "PT_LONG:PSETID_Appointment:0x8218"; - $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503"; - $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501"; - $properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a"; - $properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586"; - $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords"; - $properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502"; - $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516"; - $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517"; - $properties["basedate"] = "PT_SYSTIME:PSETID_Appointment:0x8228"; - $properties["timezone_data"] = "PT_BINARY:PSETID_Appointment:0x8233"; - $this->proptags = getPropIdsFromStrings($store, $properties); - - $this->session = $session; - $this->calendar = $calendar; - $this->entryid = $entryid; - $this->store = $store; - } - - /** - * Publishes the infomation - * @paam timestamp $starttime Time from which to publish data (usually now) - * @paam integer $length Amount of seconds from $starttime we should publish - */ - function publishFB($starttime, $length) { - $start = $starttime; - $end = $starttime + $length; - - // Get all the items in the calendar that we need - - $calendaritems = Array(); - - $restrict = Array(RES_OR, - Array( - // OR - // (item[start] >= start && item[start] <= end) - Array(RES_AND, - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_GE, - ULPROPTAG => $this->proptags["startdate"], - VALUE => $start - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_LE, - ULPROPTAG => $this->proptags["startdate"], - VALUE => $end - ) - ) - ) - ), - // OR - // (item[end] >= start && item[end] <= end) - Array(RES_AND, - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_GE, - ULPROPTAG => $this->proptags["duedate"], - VALUE => $start - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_LE, - ULPROPTAG => $this->proptags["duedate"], - VALUE => $end - ) - ) - ) - ), - // OR - // (item[start] < start && item[end] > end) - Array(RES_AND, - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_LT, - ULPROPTAG => $this->proptags["startdate"], - VALUE => $start - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_GT, - ULPROPTAG => $this->proptags["duedate"], - VALUE => $end - ) - ) - ) - ), - // OR - Array(RES_OR, - Array( - // OR - // (EXIST(ecurrence_enddate_property) && item[isRecurring] == true && item[end] >= start) - Array(RES_AND, - Array( - Array(RES_EXIST, - Array(ULPROPTAG => $this->proptags["enddate_recurring"], - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => $this->proptags["recurring"], - VALUE => true - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_GE, - ULPROPTAG => $this->proptags["enddate_recurring"], - VALUE => $start - ) - ) - ) - ), - // OR - // (!EXIST(ecurrence_enddate_property) && item[isRecurring] == true && item[start] <= end) - Array(RES_AND, - Array( - Array(RES_NOT, - Array( - Array(RES_EXIST, - Array(ULPROPTAG => $this->proptags["enddate_recurring"] - ) - ) - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_LE, - ULPROPTAG => $this->proptags["startdate"], - VALUE => $end - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => $this->proptags["recurring"], - VALUE => true - ) - ) - ) - ) - ) - ) // EXISTS OR - ) - ); // global OR - - $contents = mapi_folder_getcontentstable($this->calendar); - mapi_table_restrict($contents, $restrict); - - while(1) { - $rows = mapi_table_queryrows($contents, array_values($this->proptags), 0, 50); - - if(!is_array($rows)) - break; - - if(empty($rows)) - break; - - foreach ($rows as $row) { - $occurrences = Array(); - if(isset($row[$this->proptags['recurring']]) && $row[$this->proptags['recurring']]) { - $recur = new Recurrence($this->store, $row); - - $occurrences = $recur->getItems($starttime, $starttime + $length); - } else { - $occurrences[] = $row; - } - - $calendaritems = array_merge($calendaritems, $occurrences); - } - } - - // $calendaritems now contains all the calendar items in the specified time - // frame. We now need to merge these into a flat array of begin/end/status - // objects. This also filters out all the 'free' items (status 0) - - $freebusy = $this->mergeItemsFB($calendaritems); - - // $freebusy now contains the start, end and status of all items, merged. - - // Get the FB interface - try { - $fbsupport = mapi_freebusysupport_open($this->session, $this->store); - } catch (MAPIException $e) { - if($e->getCode() == MAPI_E_NOT_FOUND) { - $e->setHandled(); - if(function_exists("dump")) { - dump("Error in opening freebusysupport object."); - } - } - } - - // Open updater for this user - if(isset($fbsupport) && $fbsupport) { - $updaters = mapi_freebusysupport_loadupdate($fbsupport, Array($this->entryid)); - - $updater = $updaters[0]; - - // Send the data - mapi_freebusyupdate_reset($updater); - mapi_freebusyupdate_publish($updater, $freebusy); - mapi_freebusyupdate_savechanges($updater, $start-24*60*60, $end); - - // We're finished - mapi_freebusysupport_close($fbsupport); - } - else - ZLog::Write(LOGLEVEL_WARN, "FreeBusyPublish is not available"); - } - - /** - * Sorts by timestamp, if equal, then end before start - */ - function cmp($a, $b) - { - if ($a["time"] == $b["time"]) { - if($a["type"] < $b["type"]) - return 1; - if($a["type"] > $b["type"]) - return -1; - return 0; - } - return ($a["time"] > $b["time"] ? 1 : -1); - } - - /** - * Function mergeItems - * @author Steve Hardy - */ - function mergeItemsFB($items) - { - $merged = Array(); - $timestamps = Array(); - $csubj = Array(); - $cbusy = Array(); - $level = 0; - $laststart = null; - - foreach($items as $item) - { - $ts["type"] = 0; - $ts["time"] = $item[$this->proptags["startdate"]]; - $ts["subject"] = $item[PR_SUBJECT]; - $ts["status"] = (isset($item[$this->proptags["busystatus"]])) ? $item[$this->proptags["busystatus"]] : 0; //ZP-197 - $timestamps[] = $ts; - - $ts["type"] = 1; - $ts["time"] = $item[$this->proptags["duedate"]]; - $ts["subject"] = $item[PR_SUBJECT]; - $ts["status"] = (isset($item[$this->proptags["busystatus"]])) ? $item[$this->proptags["busystatus"]] : 0; //ZP-197 - $timestamps[] = $ts; - } - - usort($timestamps, Array($this, "cmp")); - $laststart = 0; // seb added - - foreach($timestamps as $ts) - { - switch ($ts["type"]) - { - case 0: // Start - if ($level != 0 && $laststart != $ts["time"]) - { - $newitem["start"] = $laststart; - $newitem["end"] = $ts["time"]; - $newitem["subject"] = join(",", $csubj); - $newitem["status"] = !empty($cbusy) ? max($cbusy) : 0; - if($newitem["status"] > 0) - $merged[] = $newitem; - } - - $level++; - - $csubj[] = $ts["subject"]; - $cbusy[] = $ts["status"]; - - $laststart = $ts["time"]; - break; - case 1: // End - if ($laststart != $ts["time"]) - { - $newitem["start"] = $laststart; - $newitem["end"] = $ts["time"]; - $newitem["subject"] = join(",", $csubj); - $newitem["status"] = !empty($cbusy) ? max($cbusy) : 0; - if($newitem["status"] > 0) - $merged[] = $newitem; - } - - $level--; - - array_splice($csubj, array_search($ts["subject"], $csubj, 1), 1); - array_splice($cbusy, array_search($ts["status"], $cbusy, 1), 1); - - $laststart = $ts["time"]; - break; - } - } - - return $merged; - } - -} diff --git a/sources/backend/zarafa/mapi/class.mapiexception.php b/sources/backend/zarafa/mapi/class.mapiexception.php deleted file mode 100644 index 6b7d76b..0000000 --- a/sources/backend/zarafa/mapi/class.mapiexception.php +++ /dev/null @@ -1,106 +0,0 @@ -. - * - */ - - - /** - * MAPIException - * if enabled using mapi_enable_exceptions then php-ext can throw exceptions when - * any error occurs in mapi calls. this exception will only be thrown when severity bit is set in - * error code that means it will be thrown only for mapi errors not for mapi warnings. - */ - // FatalException will trigger a HTTP return code 500 to the mobile - class MAPIException extends FatalException - { - /** - * Function will return display message of exception if its set by the calle. - * if it is not set then we are generating some default display messages based - * on mapi error code. - * @return string returns error-message that should be sent to client to display. - */ - public function getDisplayMessage() - { - if(!empty($this->displayMessage)) - return $this->displayMessage; - - switch($this->getCode()) - { - case MAPI_E_NO_ACCESS: - return _("You have insufficient privileges to open this object."); - case MAPI_E_LOGON_FAILED: - case MAPI_E_UNCONFIGURED: - return _("Logon Failed. Please check your username/password."); - case MAPI_E_NETWORK_ERROR: - return _("Can not connect to Zarafa server."); - case MAPI_E_UNKNOWN_ENTRYID: - return _("Can not open object with provided id."); - case MAPI_E_NO_RECIPIENTS: - return _("There are no recipients in the message."); - case MAPI_E_NOT_FOUND: - return _("Can not find object."); - case MAPI_E_INTERFACE_NOT_SUPPORTED: - case MAPI_E_INVALID_PARAMETER: - case MAPI_E_INVALID_ENTRYID: - case MAPI_E_INVALID_OBJECT: - case MAPI_E_TOO_COMPLEX: - case MAPI_E_CORRUPT_DATA: - case MAPI_E_END_OF_SESSION: - case MAPI_E_AMBIGUOUS_RECIP: - case MAPI_E_COLLISION: - case MAPI_E_UNCONFIGURED: - default : - return sprintf(_("Unknown MAPI Error: %s"), get_mapi_error_name($this->getCode())); - } - } - } - - // Tell the PHP extension which exception class to instantiate - if (function_exists('mapi_enable_exceptions')) { - //mapi_enable_exceptions("mapiexception"); - } diff --git a/sources/backend/zarafa/mapi/class.meetingrequest.php b/sources/backend/zarafa/mapi/class.meetingrequest.php deleted file mode 100644 index 3c4195c..0000000 --- a/sources/backend/zarafa/mapi/class.meetingrequest.php +++ /dev/null @@ -1,3197 +0,0 @@ -. - * - */ - -class Meetingrequest { - /* - * NOTE - * - * This class is designed to modify and update meeting request properties - * and to search for linked appointments in the calendar. It does not - * - set standard properties like subject or location - * - commit property changes through savechanges() (except in accept() and decline()) - * - * To set all the other properties, just handle the item as any other appointment - * item. You aren't even required to set those properties before or after using - * this class. If you update properties before REsending a meeting request (ie with - * a time change) you MUST first call updateMeetingRequest() so the internal counters - * can be updated. You can then submit the message any way you like. - * - */ - - /* - * How to use - * ---------- - * - * Sending a meeting request: - * - Create appointment item as normal, but as 'tentative' - * (this is the state of the item when the receiving user has received but - * not accepted the item) - * - Set recipients as normally in e-mails - * - Create Meetingrequest class instance - * - Call setMeetingRequest(), this turns on all the meeting request properties in the - * calendar item - * - Call sendMeetingRequest(), this sends a copy of the item with some extra properties - * - * Updating a meeting request: - * - Create Meetingrequest class instance - * - Call updateMeetingRequest(), this updates the counters - * - Call sendMeetingRequest() - * - * Clicking on a an e-mail: - * - Create Meetingrequest class instance - * - Check isMeetingRequest(), if true: - * - Check isLocalOrganiser(), if true then ignore the message - * - Check isInCalendar(), if not call doAccept(true, false, false). This adds the item in your - * calendar as tentative without sending a response - * - Show Accept, Tentative, Decline buttons - * - When the user presses Accept, Tentative or Decline, call doAccept(false, true, true), - * doAccept(true, true, true) or doDecline(true) respectively to really accept or decline and - * send the response. This will remove the request from your inbox. - * - Check isMeetingRequestResponse, if true: - * - Check isLocalOrganiser(), if not true then ignore the message - * - Call processMeetingRequestResponse() - * This will update the trackstatus of all recipients, and set the item to 'busy' - * when all the recipients have accepted. - * - Check isMeetingCancellation(), if true: - * - Check isLocalOrganiser(), if true then ignore the message - * - Check isInCalendar(), if not, then ignore - * Call processMeetingCancellation() - * - Show 'Remove item' button to user - * - When userpresses button, call doCancel(), which removes the item from your - * calendar and deletes the message - */ - - // All properties for a recipient that are interesting - var $recipprops = Array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID, PR_OBJECT_TYPE, PR_SEARCH_KEY); - - /** - * Indication whether the setting of resources in a Meeting Request is success (false) or if it - * has failed (integer). - */ - var $errorSetResource; - - /** - * Constructor - * - * Takes a store and a message. The message is an appointment item - * that should be converted into a meeting request or an incoming - * e-mail message that is a meeting request. - * - * The $session variable is optional, but required if the following features - * are to be used: - * - * - Sending meeting requests for meetings that are not in your own store - * - Sending meeting requests to resources, resource availability checking and resource freebusy updates - */ - - function Meetingrequest($store, $message, $session = false, $enableDirectBooking = true) - { - $this->store = $store; - $this->message = $message; - $this->session = $session; - // This variable string saves time information for the MR. - $this->meetingTimeInfo = false; - $this->enableDirectBooking = $enableDirectBooking; - - $properties["goid"] = "PT_BINARY:PSETID_Meeting:0x3"; - $properties["goid2"] = "PT_BINARY:PSETID_Meeting:0x23"; - $properties["type"] = "PT_STRING8:PSETID_Meeting:0x24"; - $properties["meetingrecurring"] = "PT_BOOLEAN:PSETID_Meeting:0x5"; - $properties["unknown2"] = "PT_BOOLEAN:PSETID_Meeting:0xa"; - $properties["attendee_critical_change"] = "PT_SYSTIME:PSETID_Meeting:0x1"; - $properties["owner_critical_change"] = "PT_SYSTIME:PSETID_Meeting:0x1a"; - $properties["meetingstatus"] = "PT_LONG:PSETID_Appointment:0x8217"; - $properties["responsestatus"] = "PT_LONG:PSETID_Appointment:0x8218"; - $properties["unknown6"] = "PT_LONG:PSETID_Meeting:0x4"; - $properties["replytime"] = "PT_SYSTIME:PSETID_Appointment:0x8220"; - $properties["usetnef"] = "PT_BOOLEAN:PSETID_Common:0x8582"; - $properties["recurrence_data"] = "PT_BINARY:PSETID_Appointment:0x8216"; - $properties["reminderminutes"] = "PT_LONG:PSETID_Common:0x8501"; - $properties["reminderset"] = "PT_BOOLEAN:PSETID_Common:0x8503"; - $properties["sendasical"] = "PT_BOOLEAN:PSETID_Appointment:0x8200"; - $properties["updatecounter"] = "PT_LONG:PSETID_Appointment:0x8201"; // AppointmentSequenceNumber - $properties["last_updatecounter"] = "PT_LONG:PSETID_Appointment:0x8203"; // AppointmentLastSequence - $properties["unknown7"] = "PT_LONG:PSETID_Appointment:0x8202"; - $properties["busystatus"] = "PT_LONG:PSETID_Appointment:0x8205"; - $properties["intendedbusystatus"] = "PT_LONG:PSETID_Appointment:0x8224"; - $properties["start"] = "PT_SYSTIME:PSETID_Appointment:0x820d"; - $properties["responselocation"] = "PT_STRING8:PSETID_Meeting:0x2"; - $properties["location"] = "PT_STRING8:PSETID_Appointment:0x8208"; - $properties["requestsent"] = "PT_BOOLEAN:PSETID_Appointment:0x8229"; // PidLidFInvited, MeetingRequestWasSent - $properties["startdate"] = "PT_SYSTIME:PSETID_Appointment:0x820d"; - $properties["duedate"] = "PT_SYSTIME:PSETID_Appointment:0x820e"; - $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516"; - $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517"; - $properties["recurring"] = "PT_BOOLEAN:PSETID_Appointment:0x8223"; - $properties["clipstart"] = "PT_SYSTIME:PSETID_Appointment:0x8235"; - $properties["clipend"] = "PT_SYSTIME:PSETID_Appointment:0x8236"; - $properties["start_recur_date"] = "PT_LONG:PSETID_Meeting:0xD"; // StartRecurTime - $properties["start_recur_time"] = "PT_LONG:PSETID_Meeting:0xE"; // StartRecurTime - $properties["end_recur_date"] = "PT_LONG:PSETID_Meeting:0xF"; // EndRecurDate - $properties["end_recur_time"] = "PT_LONG:PSETID_Meeting:0x10"; // EndRecurTime - $properties["is_exception"] = "PT_BOOLEAN:PSETID_Meeting:0xA"; // LID_IS_EXCEPTION - $properties["apptreplyname"] = "PT_STRING8:PSETID_Appointment:0x8230"; - // Propose new time properties - $properties["proposed_start_whole"] = "PT_SYSTIME:PSETID_Appointment:0x8250"; - $properties["proposed_end_whole"] = "PT_SYSTIME:PSETID_Appointment:0x8251"; - $properties["proposed_duration"] = "PT_LONG:PSETID_Appointment:0x8256"; - $properties["counter_proposal"] = "PT_BOOLEAN:PSETID_Appointment:0x8257"; - $properties["recurring_pattern"] = "PT_STRING8:PSETID_Appointment:0x8232"; - $properties["basedate"] = "PT_SYSTIME:PSETID_Appointment:0x8228"; - $properties["meetingtype"] = "PT_LONG:PSETID_Meeting:0x26"; - $properties["timezone_data"] = "PT_BINARY:PSETID_Appointment:0x8233"; - $properties["timezone"] = "PT_STRING8:PSETID_Appointment:0x8234"; - $properties["toattendeesstring"] = "PT_STRING8:PSETID_Appointment:0x823B"; - $properties["ccattendeesstring"] = "PT_STRING8:PSETID_Appointment:0x823C"; - $this->proptags = getPropIdsFromStrings($store, $properties); - } - - /** - * Sets the direct booking property. This is an alternative to the setting of the direct booking - * property through the constructor. However, setting it in the constructor is prefered. - * @param Boolean $directBookingSetting - * - */ - function setDirectBooking($directBookingSetting) - { - $this->enableDirectBooking = $directBookingSetting; - } - - /** - * Returns TRUE if the message pointed to is an incoming meeting request and should - * therefore be replied to with doAccept or doDecline() - */ - function isMeetingRequest() - { - $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS)); - - if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request") - return true; - } - - /** - * Returns TRUE if the message pointed to is a returning meeting request response - */ - function isMeetingRequestResponse() - { - $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS)); - - if(isset($props[PR_MESSAGE_CLASS]) && strpos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp") === 0) - return true; - } - - /** - * Returns TRUE if the message pointed to is a cancellation request - */ - function isMeetingCancellation() - { - $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS)); - - if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Canceled") - return true; - } - - - /** - * Process an incoming meeting request response as Delegate. This will updates the appointment - * in Organiser's calendar. - * @returns the entryids(storeid, parententryid, entryid, also basedate if response is occurrence) - * of corresponding meeting in Calendar - */ - function processMeetingRequestResponseAsDelegate() - { - if(!$this->isMeetingRequestResponse()) - return; - - $messageprops = mapi_getprops($this->message); - - $goid2 = $messageprops[$this->proptags['goid2']]; - - if(!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS])) - return; - - // Find basedate in GlobalID(0x3), this can be a response for an occurrence - $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]); - - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) { - $delegatorStore = $this->getDelegatorStore($messageprops); - $userStore = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - - if($calFolder){ - $calendaritems = $this->findCalendarItems($goid2, $calFolder); - - // $calendaritems now contains the ENTRYID's of all the calendar items to which - // this meeting request points. - - // Open the calendar items, and update all the recipients of the calendar item that match - // the email address of the response. - if (!empty($calendaritems)) { - return $this->processResponse($userStore, $calendaritems[0], $basedate, $messageprops); - }else{ - return false; - } - } - } - } - - - /** - * Process an incoming meeting request response. This updates the appointment - * in your calendar to show whether the user has accepted or declined. - * @returns the entryids(storeid, parententryid, entryid, also basedate if response is occurrence) - * of corresponding meeting in Calendar - */ - function processMeetingRequestResponse() - { - if(!$this->isLocalOrganiser()) - return; - - if(!$this->isMeetingRequestResponse()) - return; - - // Get information we need from the response message - $messageprops = mapi_getprops($this->message, Array( - $this->proptags['goid'], - $this->proptags['goid2'], - PR_OWNER_APPT_ID, - PR_SENT_REPRESENTING_EMAIL_ADDRESS, - PR_SENT_REPRESENTING_NAME, - PR_SENT_REPRESENTING_ADDRTYPE, - PR_SENT_REPRESENTING_ENTRYID, - PR_MESSAGE_DELIVERY_TIME, - PR_MESSAGE_CLASS, - PR_PROCESSED, - $this->proptags['proposed_start_whole'], - $this->proptags['proposed_end_whole'], - $this->proptags['proposed_duration'], - $this->proptags['counter_proposal'], - $this->proptags['attendee_critical_change'])); - - $goid2 = $messageprops[$this->proptags['goid2']]; - - if(!isset($goid2) || !isset($messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS])) - return; - - // Find basedate in GlobalID(0x3), this can be a response for an occurrence - $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]); - - $calendaritems = $this->findCalendarItems($goid2); - - // $calendaritems now contains the ENTRYID's of all the calendar items to which - // this meeting request points. - - // Open the calendar items, and update all the recipients of the calendar item that match - // the email address of the response. - if (!empty($calendaritems)) { - return $this->processResponse($this->store, $calendaritems[0], $basedate, $messageprops); - }else{ - return false; - } - } - - /** - * Process every incoming MeetingRequest response.This updates the appointment - * in your calendar to show whether the user has accepted or declined. - *@param resource $store contains the userStore in which the meeting is created - *@param $entryid contains the ENTRYID of the calendar items to which this meeting request points. - *@param boolean $basedate if present the create an exception - *@param array $messageprops contains m3/17/2010essage properties. - *@return entryids(storeid, parententryid, entryid, also basedate if response is occurrence) of corresponding meeting in Calendar - */ - function processResponse($store, $entryid, $basedate, $messageprops) - { - $data = array(); - $senderentryid = $messageprops[PR_SENT_REPRESENTING_ENTRYID]; - $messageclass = $messageprops[PR_MESSAGE_CLASS]; - $deliverytime = $messageprops[PR_MESSAGE_DELIVERY_TIME]; - - // Open the calendar item, find the sender in the recipient table and update all the recipients of the calendar item that match - // the email address of the response. - $calendaritem = mapi_msgstore_openentry($store, $entryid); - $calendaritemProps = mapi_getprops($calendaritem, array($this->proptags['recurring'], PR_STORE_ENTRYID, PR_PARENT_ENTRYID, PR_ENTRYID, $this->proptags['updatecounter'])); - - $data["storeid"] = bin2hex($calendaritemProps[PR_STORE_ENTRYID]); - $data["parententryid"] = bin2hex($calendaritemProps[PR_PARENT_ENTRYID]); - $data["entryid"] = bin2hex($calendaritemProps[PR_ENTRYID]); - $data["basedate"] = $basedate; - $data["updatecounter"] = isset($calendaritemProps[$this->proptags['updatecounter']]) ? $calendaritemProps[$this->proptags['updatecounter']] : 0; - - /** - * Check if meeting is updated or not in organizer's calendar - */ - $data["meeting_updated"] = $this->isMeetingUpdated(); - - if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) { - // meeting is already processed - return $data; - } else { - mapi_setprops($this->message, Array(PR_PROCESSED => true)); - mapi_savechanges($this->message); - } - - // if meeting is updated in organizer's calendar then we don't need to process - // old response - if($data['meeting_updated'] === true) { - return $data; - } - - // If basedate is found, then create/modify exception msg and do processing - if ($basedate && $calendaritemProps[$this->proptags['recurring']]) { - $recurr = new Recurrence($store, $calendaritem); - - // Copy properties from meeting request - $exception_props = mapi_getprops($this->message, array(PR_OWNER_APPT_ID, - $this->proptags['proposed_start_whole'], - $this->proptags['proposed_end_whole'], - $this->proptags['proposed_duration'], - $this->proptags['counter_proposal'] - )); - - // Create/modify exception - if($recurr->isException($basedate)) { - $recurr->modifyException($exception_props, $basedate); - } else { - // When we are creating an exception we need copy recipients from main recurring item - $recipTable = mapi_message_getrecipienttable($calendaritem); - $recips = mapi_table_queryallrows($recipTable, $this->recipprops); - - // Retrieve actual start/due dates from calendar item. - $exception_props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate); - $exception_props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate); - - $recurr->createException($exception_props, $basedate, false, $recips); - } - - mapi_message_savechanges($calendaritem); - - $attach = $recurr->getExceptionAttachment($basedate); - if ($attach) { - $recurringItem = $calendaritem; - $calendaritem = mapi_attach_openobj($attach, MAPI_MODIFY); - } else { - return false; - } - } - - // Get the recipients of the calendar item - $reciptable = mapi_message_getrecipienttable($calendaritem); - $recipients = mapi_table_queryallrows($reciptable, $this->recipprops); - - // FIXME we should look at the updatecounter property and compare it - // to the counter in the recipient to see if this update is actually - // newer than the status in the calendar item - $found = false; - - $totalrecips = 0; - $acceptedrecips = 0; - foreach($recipients as $recipient) { - $totalrecips++; - if(isset($recipient[PR_ENTRYID]) && $this->compareABEntryIDs($recipient[PR_ENTRYID],$senderentryid)) { - $found = true; - - /** - * If value of attendee_critical_change on meeting response mail is less than PR_RECIPIENT_TRACKSTATUS_TIME - * on the corresponding recipientRow of meeting then we ignore this response mail. - */ - if (isset($recipient[PR_RECIPIENT_TRACKSTATUS_TIME]) && ($messageprops[$this->proptags['attendee_critical_change']] < $recipient[PR_RECIPIENT_TRACKSTATUS_TIME])) { - continue; - } - - // The email address matches, update the row - $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass); - $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $messageprops[$this->proptags['attendee_critical_change']]; - - // If this is a counter proposal, set the proposal properties in the recipient row - if(isset($messageprops[$this->proptags['counter_proposal']]) && $messageprops[$this->proptags['counter_proposal']]){ - $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']]; - $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']]; - $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']]; - } - - mapi_message_modifyrecipients($calendaritem, MODRECIP_MODIFY, Array($recipient)); - } - if(isset($recipient[PR_RECIPIENT_TRACKSTATUS]) && $recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) - $acceptedrecips++; - } - - // If the recipient was not found in the original calendar item, - // then add the recpient as a new optional recipient - if(!$found) { - $recipient = Array(); - $recipient[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID]; - $recipient[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS]; - $recipient[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME]; - $recipient[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE]; - $recipient[PR_RECIPIENT_TYPE] = MAPI_CC; - $recipient[PR_RECIPIENT_TRACKSTATUS] = $this->getTrackStatus($messageclass); - $recipient[PR_RECIPIENT_TRACKSTATUS_TIME] = $deliverytime; - - // If this is a counter proposal, set the proposal properties in the recipient row - if(isset($messageprops[$this->proptags['counter_proposal']])){ - $recipient[PR_PROPOSENEWTIME_START] = $messageprops[$this->proptags['proposed_start_whole']]; - $recipient[PR_PROPOSENEWTIME_END] = $messageprops[$this->proptags['proposed_end_whole']]; - $recipient[PR_PROPOSEDNEWTIME] = $messageprops[$this->proptags['counter_proposal']]; - } - - mapi_message_modifyrecipients($calendaritem, MODRECIP_ADD, Array($recipient)); - $totalrecips++; - if($recipient[PR_RECIPIENT_TRACKSTATUS] == olRecipientTrackStatusAccepted) - $acceptedrecips++; - } - -//TODO: Upate counter proposal number property on message -/* -If it is the first time this attendee has proposed a new date/time, increment the value of the PidLidAppointmentProposalNumber property on the organizer�s meeting object, by 0x00000001. If this property did not previously exist on the organizer�s meeting object, it MUST be set with a value of 0x00000001. -*/ - // If this is a counter proposal, set the counter proposal indicator boolean - if(isset($messageprops[$this->proptags['counter_proposal']])){ - $props = Array(); - if($messageprops[$this->proptags['counter_proposal']]){ - $props[$this->proptags['counter_proposal']] = true; - }else{ - $props[$this->proptags['counter_proposal']] = false; - } - - mapi_message_setprops($calendaritem, $props); - } - - mapi_message_savechanges($calendaritem); - if (isset($attach)) { - mapi_message_savechanges($attach); - mapi_message_savechanges($recurringItem); - } - - return $data; - } - - - /** - * Process an incoming meeting request cancellation. This updates the - * appointment in your calendar to show that the meeting has been cancelled. - */ - function processMeetingCancellation() - { - if($this->isLocalOrganiser()) - return; - - if(!$this->isMeetingCancellation()) - return; - - if(!$this->isInCalendar()) - return; - - $listProperties = $this->proptags; - $listProperties['subject'] = PR_SUBJECT; - $listProperties['sent_representing_name'] = PR_SENT_REPRESENTING_NAME; - $listProperties['sent_representing_address_type'] = PR_SENT_REPRESENTING_ADDRTYPE; - $listProperties['sent_representing_email_address'] = PR_SENT_REPRESENTING_EMAIL_ADDRESS; - $listProperties['sent_representing_entryid'] = PR_SENT_REPRESENTING_ENTRYID; - $listProperties['sent_representing_search_key'] = PR_SENT_REPRESENTING_SEARCH_KEY; - $listProperties['rcvd_representing_name'] = PR_RCVD_REPRESENTING_NAME; - $messageprops = mapi_getprops($this->message, $listProperties); - $store = $this->store; - - $goid = $messageprops[$this->proptags['goid']]; //GlobalID (0x3) - if(!isset($goid)) - return; - - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])){ - $delegatorStore = $this->getDelegatorStore($messageprops); - $store = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - } - - // First, find the items in the calendar by GOID - $calendaritems = $this->findCalendarItems($goid, $calFolder); - $basedate = $this->getBasedateFromGlobalID($goid); - - if ($basedate) { - // Calendaritems with GlobalID were not found, so find main recurring item using CleanGlobalID(0x23) - if (empty($calendaritems)) { - // This meeting req is of an occurrance - $goid2 = $messageprops[$this->proptags['goid2']]; - - // First, find the items in the calendar by GOID - $calendaritems = $this->findCalendarItems($goid2); - foreach($calendaritems as $entryid) { - // Open each calendar item and set the properties of the cancellation object - $calendaritem = mapi_msgstore_openentry($store, $entryid); - - if ($calendaritem){ - $calendaritemProps = mapi_getprops($calendaritem, array($this->proptags['recurring'])); - if ($calendaritemProps[$this->proptags['recurring']]){ - $recurr = new Recurrence($store, $calendaritem); - - // Set message class - $messageprops[PR_MESSAGE_CLASS] = 'IPM.Appointment'; - - if($recurr->isException($basedate)) - $recurr->modifyException($messageprops, $basedate); - else - $recurr->createException($messageprops, $basedate); - } - mapi_savechanges($calendaritem); - } - } - } - } - - if (!isset($calendaritem)) { - foreach($calendaritems as $entryid) { - // Open each calendar item and set the properties of the cancellation object - $calendaritem = mapi_msgstore_openentry($store, $entryid); - mapi_message_setprops($calendaritem, $messageprops); - mapi_savechanges($calendaritem); - } - } - } - - /** - * Returns true if the item is already in the calendar - */ - function isInCalendar() { - $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME)); - $goid = $messageprops[$this->proptags['goid']]; - if (isset($messageprops[$this->proptags['goid2']])) - $goid2 = $messageprops[$this->proptags['goid2']]; - - $basedate = $this->getBasedateFromGlobalID($goid); - - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])){ - $delegatorStore = $this->getDelegatorStore($messageprops); - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - } - /** - * If basedate is found in globalID, then there are two possibilities. - * case 1) User has only this occurrence OR - * case 2) User has recurring item and has received an update for an occurrence - */ - if ($basedate) { - // First try with GlobalID(0x3) (case 1) - $entryid = $this->findCalendarItems($goid, $calFolder); - // If not found then try with CleanGlobalID(0x23) (case 2) - if (!is_array($entryid) && isset($goid2)) - $entryid = $this->findCalendarItems($goid2, $calFolder); - } else if (isset($goid2)) { - $entryid = $this->findCalendarItems($goid2, $calFolder); - } - else - return false; - - return is_array($entryid); - } - - /** - * Accepts the meeting request by moving the item to the calendar - * and sending a confirmation message back to the sender. If $tentative - * is TRUE, then the item is accepted tentatively. After accepting, you - * can't use this class instance any more. The message is closed. If you - * specify TRUE for 'move', then the item is actually moved (from your - * inbox probably) to the calendar. If you don't, it is copied into - * your calendar. - *@param boolean $tentative true if user as tentative accepted the meeting - *@param boolean $sendresponse true if a response has to be send to organizer - *@param boolean $move true if the meeting request should be moved to the deleted items after processing - *@param string $newProposedStartTime contains starttime if user has proposed other time - *@param string $newProposedEndTime contains endtime if user has proposed other time - *@param string $basedate start of day of occurrence for which user has accepted the recurrent meeting - *@return string $entryid entryid of item which created/updated in calendar - */ - function doAccept($tentative, $sendresponse, $move, $newProposedStartTime=false, $newProposedEndTime=false, $body=false, $userAction = false, $store=false, $basedate = false) - { - if($this->isLocalOrganiser()) - return false; - - // Remove any previous calendar items with this goid and appt id - $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID, PR_MESSAGE_CLASS, $this->proptags['goid'], $this->proptags['goid2'], PR_OWNER_APPT_ID, $this->proptags['updatecounter'], PR_PROCESSED, $this->proptags['recurring'], $this->proptags['intendedbusystatus'], PR_RCVD_REPRESENTING_NAME)); - - /** - * if this function is called automatically with meeting request object then there will be - * two possibilitites - * 1) meeting request is opened first time, in this case make a tentative appointment in - recipient's calendar - * 2) after this every subsequest request to open meeting request will not do any processing - */ - if($messageprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request" && $userAction == false) { - if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED] == true) { - // if meeting request is already processed then don't do anything - return false; - } else { - mapi_setprops($this->message, Array(PR_PROCESSED => true)); - mapi_message_savechanges($this->message); - } - } - - // If this meeting request is received by a delegate then open delegator's store. - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) { - $delegatorStore = $this->getDelegatorStore($messageprops); - - $store = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - $store = $this->store; - } - - return $this->accept($tentative, $sendresponse, $move, $newProposedStartTime, $newProposedEndTime, $body, $userAction, $store, $calFolder, $basedate); - } - - function accept($tentative, $sendresponse, $move, $newProposedStartTime=false, $newProposedEndTime=false, $body=false, $userAction = false, $store, $calFolder, $basedate = false) - { - $messageprops = mapi_getprops($this->message); - $isDelegate = false; - - if (isset($messageprops[PR_DELEGATED_BY_RULE])) - $isDelegate = true; - - $goid = $messageprops[$this->proptags['goid2']]; - - // Retrieve basedate from globalID, if it is not recieved as argument - if (!$basedate) - $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]); - - if ($sendresponse) - $this->createResponse($tentative ? olResponseTentative : olResponseAccepted, $newProposedStartTime, $newProposedEndTime, $body, $store, $basedate, $calFolder); - - $entryids = $this->findCalendarItems($goid, $calFolder); - - if(is_array($entryids)) { - // Only check the first, there should only be one anyway... - $previtem = mapi_msgstore_openentry($store, $entryids[0]); - $prevcounterprops = mapi_getprops($previtem, array($this->proptags['updatecounter'])); - - // Check if the existing item has an updatecounter that is lower than the request we are processing. If not, then we ignore this call, since the - // meeting request is out of date. - /* - if(message_counter < appointment_counter) do_nothing - if(message_counter == appointment_counter) do_something_if_the_user_tells_us (userAction == true) - if(message_counter > appointment_counter) do_something_even_automatically - */ - if(isset($prevcounterprops[$this->proptags['updatecounter']]) && $messageprops[$this->proptags['updatecounter']] < $prevcounterprops[$this->proptags['updatecounter']]) { - return false; - } else if(isset($prevcounterprops[$this->proptags['updatecounter']]) && $messageprops[$this->proptags['updatecounter']] == $prevcounterprops[$this->proptags['updatecounter']]) { - if($userAction == false && !$basedate) { - return false; - } - } - } - - // set counter proposal properties in calendar item when proposing new time - // @FIXME this can be moved before call to createResponse function so that function doesn't need to recalculate duration - $proposeNewTimeProps = array(); - if($newProposedStartTime && $newProposedEndTime) { - $proposeNewTimeProps[$this->proptags['proposed_start_whole']] = $newProposedStartTime; - $proposeNewTimeProps[$this->proptags['proposed_end_whole']] = $newProposedEndTime; - $proposeNewTimeProps[$this->proptags['proposed_duration']] = round($newProposedEndTime - $newProposedStartTime) / 60; - $proposeNewTimeProps[$this->proptags['counter_proposal']] = true; - } - - /** - * Further processing depends on what user is receiving. User can receive recurring item, a single occurrence or a normal meeting. - * 1) If meeting req is of recurrence then we find all the occurrence in calendar because in past user might have recivied one or few occurrences. - * 2) If single occurrence then find occurrence itself using globalID and if item is not found then user cleanGlobalID to find main recurring item - * 3) Normal meeting req are handled normally has they were handled previously. - * - * Also user can respond(accept/decline) to item either from previewpane or from calendar by opening the item. If user is responding the meeting from previewpane - * and that item is not found in calendar then item is move else item is opened and all properties, attachments and recipient are copied from meeting request. - * If user is responding from calendar then item is opened and properties are set such as meetingstatus, responsestatus, busystatus etc. - */ - if ($messageprops[PR_MESSAGE_CLASS] == "IPM.Schedule.Meeting.Request") { - // While processing the item mark it as read. - mapi_message_setreadflag($this->message, SUPPRESS_RECEIPT); - - // This meeting request item is recurring, so find all occurrences and saves them all as exceptions to this meeting request item. - if ($messageprops[$this->proptags['recurring']] == true) { - $calendarItem = false; - - // Find main recurring item based on GlobalID (0x3) - $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder); - if (is_array($items)) { - foreach($items as $key => $entryid) - $calendarItem = mapi_msgstore_openentry($store, $entryid); - } - - // Recurring item not found, so create new meeting in Calendar - if (!$calendarItem) - $calendarItem = mapi_folder_createmessage($calFolder); - - // Copy properties - $props = mapi_getprops($this->message); - $props[PR_MESSAGE_CLASS] = 'IPM.Appointment'; - $props[$this->proptags['meetingstatus']] = olMeetingReceived; - // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded - $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded; - - if (isset($props[$this->proptags['intendedbusystatus']])) { - if($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) { - $props[$this->proptags['busystatus']] = $tentative; - } else { - $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']]; - } - // we already have intendedbusystatus value in $props so no need to copy it - } else { - $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy; - } - - if($userAction) { - // if user has responded then set replytime - $props[$this->proptags['replytime']] = time(); - } - - mapi_setprops($calendarItem, $props); - - // Copy attachments too - $this->replaceAttachments($this->message, $calendarItem); - // Copy recipients too - $this->replaceRecipients($this->message, $calendarItem, $isDelegate); - - // Find all occurrences based on CleanGlobalID (0x23) - $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true); - if (is_array($items)) { - // Save all existing occurrence as exceptions - foreach($items as $entryid) { - // Open occurrence - $occurrenceItem = mapi_msgstore_openentry($store, $entryid); - - // Save occurrence into main recurring item as exception - if ($occurrenceItem) { - $occurrenceItemProps = mapi_getprops($occurrenceItem, array($this->proptags['goid'], $this->proptags['recurring'])); - - // Find basedate of occurrence item - $basedate = $this->getBasedateFromGlobalID($occurrenceItemProps[$this->proptags['goid']]); - if ($basedate && $occurrenceItemProps[$this->proptags['recurring']] != true) - $this->acceptException($calendarItem, $occurrenceItem, $basedate, true, $tentative, $userAction, $store, $isDelegate); - } - } - } - mapi_savechanges($calendarItem); - if ($move) { - $wastebasket = $this->openDefaultWastebasket(); - mapi_folder_copymessages($calFolder, Array($props[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - } - $entryid = $props[PR_ENTRYID]; - } else { - /** - * This meeting request is not recurring, so can be an exception or normal meeting. - * If exception then find main recurring item and update exception - * If main recurring item is not found then put exception into Calendar as normal meeting. - */ - $calendarItem = false; - - // We found basedate in GlobalID of this meeting request, so this meeting request if for an occurrence. - if ($basedate) { - // Find main recurring item from CleanGlobalID of this meeting request - $items = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder); - if (is_array($items)) { - foreach($items as $key => $entryid) { - $calendarItem = mapi_msgstore_openentry($store, $entryid); - } - } - - // Main recurring item is found, so now update exception - if ($calendarItem) { - $this->acceptException($calendarItem, $this->message, $basedate, $move, $tentative, $userAction, $store, $isDelegate); - $calendarItemProps = mapi_getprops($calendarItem, array(PR_ENTRYID)); - $entryid = $calendarItemProps[PR_ENTRYID]; - } - } - - if (!$calendarItem) { - $items = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder); - - if (is_array($items)) - mapi_folder_deletemessages($calFolder, $items); - - if ($move) { - // All we have to do is open the default calendar, - // set the mesage class correctly to be an appointment item - // and move it to the calendar folder - $sourcefolder = $this->openParentFolder(); - - /* create a new calendar message, and copy the message to there, - since we want to delete (move to wastebasket) the original message */ - $old_entryid = mapi_getprops($this->message, Array(PR_ENTRYID)); - $calmsg = mapi_folder_createmessage($calFolder); - mapi_copyto($this->message, array(), array(), $calmsg); /* includes attachments and recipients */ - /* release old message */ - $message = null; - - $calItemProps = Array(); - $calItemProps[PR_MESSAGE_CLASS] = "IPM.Appointment"; - - if (isset($messageprops[$this->proptags['intendedbusystatus']])) { - if($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) { - $calItemProps[$this->proptags['busystatus']] = $tentative; - } else { - $calItemProps[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']]; - } - $calItemProps[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']]; - } else { - $calItemProps[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy; - } - - // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded - $calItemProps[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded; - if($userAction) { - // if user has responded then set replytime - $calItemProps[$this->proptags['replytime']] = time(); - } - - mapi_setprops($calmsg, $proposeNewTimeProps + $calItemProps); - - // get properties which stores owner information in meeting request mails - $props = mapi_getprops($calmsg, array(PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE)); - - // add owner to recipient table - $recips = array(); - $this->addOrganizer($props, $recips); - - if($isDelegate) { - /** - * If user is delegate then remove that user from recipienttable of the MR. - * and delegate MR mail doesn't contain any of the attendees in recipient table. - * So, other required and optional attendees are added from - * toattendeesstring and ccattendeesstring properties. - */ - $this->setRecipsFromString($recips, $messageprops[$this->proptags['toattendeesstring']], MAPI_TO); - $this->setRecipsFromString($recips, $messageprops[$this->proptags['ccattendeesstring']], MAPI_CC); - mapi_message_modifyrecipients($calmsg, 0, $recips); - } else { - mapi_message_modifyrecipients($calmsg, MODRECIP_ADD, $recips); - } - - mapi_message_savechanges($calmsg); - - // Move the message to the wastebasket - $wastebasket = $this->openDefaultWastebasket(); - mapi_folder_copymessages($sourcefolder, array($old_entryid[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - - $messageprops = mapi_getprops($calmsg, array(PR_ENTRYID)); - $entryid = $messageprops[PR_ENTRYID]; - } else { - // Create a new appointment with duplicate properties and recipient, but as an IPM.Appointment - $new = mapi_folder_createmessage($calFolder); - $props = mapi_getprops($this->message); - - $props[PR_MESSAGE_CLASS] = "IPM.Appointment"; - // when we are automatically processing the meeting request set responsestatus to olResponseNotResponded - $props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded; - - if (isset($props[$this->proptags['intendedbusystatus']])) { - if($tentative && $props[$this->proptags['intendedbusystatus']] !== fbFree) { - $props[$this->proptags['busystatus']] = $tentative; - } else { - $props[$this->proptags['busystatus']] = $props[$this->proptags['intendedbusystatus']]; - } - // we already have intendedbusystatus value in $props so no need to copy it - } else { - $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy; - } - - // ZP-341 - we need to copy as well the attachments - // Copy attachments too - $this->replaceAttachments($this->message, $new); - // ZP-341 - end - - if($userAction) { - // if user has responded then set replytime - $props[$this->proptags['replytime']] = time(); - } - - mapi_setprops($new, $proposeNewTimeProps + $props); - - $reciptable = mapi_message_getrecipienttable($this->message); - - $recips = array(); - if(!$isDelegate) - $recips = mapi_table_queryallrows($reciptable, $this->recipprops); - - $this->addOrganizer($props, $recips); - - if($isDelegate) { - /** - * If user is delegate then remove that user from recipienttable of the MR. - * and delegate MR mail doesn't contain any of the attendees in recipient table. - * So, other required and optional attendees are added from - * toattendeesstring and ccattendeesstring properties. - */ - $this->setRecipsFromString($recips, $messageprops[$this->proptags['toattendeesstring']], MAPI_TO); - $this->setRecipsFromString($recips, $messageprops[$this->proptags['ccattendeesstring']], MAPI_CC); - mapi_message_modifyrecipients($new, 0, $recips); - } else { - mapi_message_modifyrecipients($new, MODRECIP_ADD, $recips); - } - mapi_message_savechanges($new); - - $props = mapi_getprops($new, array(PR_ENTRYID)); - $entryid = $props[PR_ENTRYID]; - } - } - } - } else { - // Here only properties are set on calendaritem, because user is responding from calendar. - $props = array(); - $props[$this->proptags['responsestatus']] = $tentative ? olResponseTentative : olResponseAccepted; - - if (isset($messageprops[$this->proptags['intendedbusystatus']])) { - if($tentative && $messageprops[$this->proptags['intendedbusystatus']] !== fbFree) { - $props[$this->proptags['busystatus']] = $tentative; - } else { - $props[$this->proptags['busystatus']] = $messageprops[$this->proptags['intendedbusystatus']]; - } - $props[$this->proptags['intendedbusystatus']] = $messageprops[$this->proptags['intendedbusystatus']]; - } else { - $props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy; - } - - $props[$this->proptags['meetingstatus']] = olMeetingReceived; - $props[$this->proptags['replytime']] = time(); - - if ($basedate) { - $recurr = new Recurrence($store, $this->message); - - // Copy recipients list - $reciptable = mapi_message_getrecipienttable($this->message); - $recips = mapi_table_queryallrows($reciptable, $this->recipprops); - - if($recurr->isException($basedate)) { - $recurr->modifyException($proposeNewTimeProps + $props, $basedate, $recips); - } else { - $props[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate); - $props[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate); - - $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS]; - $props[PR_SENT_REPRESENTING_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME]; - $props[PR_SENT_REPRESENTING_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE]; - $props[PR_SENT_REPRESENTING_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID]; - - $recurr->createException($proposeNewTimeProps + $props, $basedate, false, $recips); - } - } else { - mapi_setprops($this->message, $proposeNewTimeProps + $props); - } - mapi_savechanges($this->message); - - $entryid = $messageprops[PR_ENTRYID]; - } - - return $entryid; - } - - /** - * Declines the meeting request by moving the item to the deleted - * items folder and sending a decline message. After declining, you - * can't use this class instance any more. The message is closed. - * When an occurrence is decline then false is returned because that - * occurrence is deleted not the recurring item. - * - *@param boolean $sendresponse true if a response has to be sent to organizer - *@param resource $store MAPI_store of user - *@param string $basedate if specified contains starttime of day of an occurrence - *@return boolean true if item is deleted from Calendar else false - */ - function doDecline($sendresponse, $store=false, $basedate = false, $body = false) - { - $result = true; - $calendaritem = false; - if($this->isLocalOrganiser()) - return; - - // Remove any previous calendar items with this goid and appt id - $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME)); - - // If this meeting request is received by a delegate then open delegator's store. - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) { - $delegatorStore = $this->getDelegatorStore($messageprops); - - $store = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - $store = $this->store; - } - - $goid = $messageprops[$this->proptags['goid']]; - - // First, find the items in the calendar by GlobalObjid (0x3) - $entryids = $this->findCalendarItems($goid, $calFolder); - - if (!$basedate) - $basedate = $this->getBasedateFromGlobalID($goid); - - if($sendresponse) - $this->createResponse(olResponseDeclined, false, false, $body, $store, $basedate, $calFolder); - - if ($basedate) { - // use CleanGlobalObjid (0x23) - $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder); - - foreach($calendaritems as $entryid) { - // Open each calendar item and set the properties of the cancellation object - $calendaritem = mapi_msgstore_openentry($store, $entryid); - - // Recurring item is found, now delete exception - if ($calendaritem) - $this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store); - } - - if ($this->isMeetingRequest()) - $calendaritem = false; - else - $result = false; - } - - if (!$calendaritem) { - $calendar = $this->openDefaultCalendar(); - - if(!empty($entryids)) { - mapi_folder_deletemessages($calendar, $entryids); - } - - // All we have to do to decline, is to move the item to the waste basket - $wastebasket = $this->openDefaultWastebasket(); - $sourcefolder = $this->openParentFolder(); - - $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID)); - - // Release the message - $this->message = null; - - // Move the message to the waste basket - mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - } - return $result; - } - - /** - * Removes a meeting request from the calendar when the user presses the - * 'remove from calendar' button in response to a meeting cancellation. - * @param string $basedate if specified contains starttime of day of an occurrence - */ - function doRemoveFromCalendar($basedate) - { - if($this->isLocalOrganiser()) - return false; - - $store = $this->store; - $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID, $this->proptags['goid'], PR_RCVD_REPRESENTING_NAME, PR_MESSAGE_CLASS)); - $goid = $messageprops[$this->proptags['goid']]; - - if (isset($messageprops[PR_RCVD_REPRESENTING_NAME])) { - $delegatorStore = $this->getDelegatorStore($messageprops); - $store = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - } - - $wastebasket = $this->openDefaultWastebasket(); - $sourcefolder = $this->openParentFolder(); - - // Check if the message is a meeting request in the inbox or a calendaritem by checking the message class - if (strpos($messageprops[PR_MESSAGE_CLASS], 'IPM.Schedule.Meeting') === 0) { - /** - * 'Remove from calendar' option from previewpane then we have to check GlobalID of this meeting request. - * If basedate found then open meeting from calendar and delete that occurence. - */ - $basedate = false; - if ($goid) { - // Retrieve GlobalID and find basedate in it. - $basedate = $this->getBasedateFromGlobalID($goid); - - // Basedate found, Now find item. - if ($basedate) { - $guid = $this->setBasedateInGlobalID($goid); - - // First, find the items in the calendar by GOID - $calendaritems = $this->findCalendarItems($guid, $calFolder); - if(is_array($calendaritems)) { - foreach($calendaritems as $entryid) { - // Open each calendar item and set the properties of the cancellation object - $calendaritem = mapi_msgstore_openentry($store, $entryid); - - if ($calendaritem){ - $this->doRemoveExceptionFromCalendar($basedate, $calendaritem, $store); - } - } - } - } - } - - // It is normal/recurring meeting item. - if (!$basedate) { - if (!isset($calFolder)) $calFolder = $this->openDefaultCalendar(); - - $entryids = $this->findCalendarItems($goid, $calFolder); - - if(is_array($entryids)){ - // Move the calendaritem to the waste basket - mapi_folder_copymessages($sourcefolder, $entryids, $wastebasket, MESSAGE_MOVE); - } - } - - // Release the message - $this->message = null; - - // Move the message to the waste basket - mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - - } else { - // Here only properties are set on calendaritem, because user is responding from calendar. - if ($basedate) { //remove the occurence - $this->doRemoveExceptionFromCalendar($basedate, $this->message, $store); - } else { //remove normal/recurring meeting item. - // Move the message to the waste basket - mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - } - } - } - - /** - * Removes the meeting request by moving the item to the deleted - * items folder. After canceling, youcan't use this class instance - * any more. The message is closed. - */ - function doCancel() - { - if($this->isLocalOrganiser()) - return; - if(!$this->isMeetingCancellation()) - return; - - // Remove any previous calendar items with this goid and appt id - $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'])); - $goid = $messageprops[$this->proptags['goid']]; - - $entryids = $this->findCalendarItems($goid); - $calendar = $this->openDefaultCalendar(); - - mapi_folder_deletemessages($calendar, $entryids); - - // All we have to do to decline, is to move the item to the waste basket - - $wastebasket = $this->openDefaultWastebasket(); - $sourcefolder = $this->openParentFolder(); - - $messageprops = mapi_getprops($this->message, Array(PR_ENTRYID)); - - // Release the message - $this->message = null; - - // Move the message to the waste basket - mapi_folder_copymessages($sourcefolder, Array($messageprops[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - } - - - /** - * Sets the properties in the message so that is can be sent - * as a meeting request. The caller has to submit the message. This - * is only used for new MeetingRequests. Pass the appointment item as $message - * in the constructor to do this. - */ - function setMeetingRequest($basedate = false) - { - $props = mapi_getprops($this->message, Array($this->proptags['updatecounter'])); - - // Create a new global id for this item - $goid = pack("H*", "040000008200E00074C5B7101A82E00800000000"); - for ($i=0; $i<36; $i++) - $goid .= chr(rand(0, 255)); - - // Create a new appointment id for this item - $apptid = rand(); - - $props[PR_OWNER_APPT_ID] = $apptid; - $props[PR_ICON_INDEX] = 1026; - $props[$this->proptags['goid']] = $goid; - $props[$this->proptags['goid2']] = $goid; - - if (!isset($props[$this->proptags['updatecounter']])) { - $props[$this->proptags['updatecounter']] = 0; // OL also starts sequence no with zero. - $props[$this->proptags['last_updatecounter']] = 0; - } - - mapi_setprops($this->message, $props); - } - - /** - * Sends a meeting request by copying it to the outbox, converting - * the message class, adding some properties that are required only - * for sending the message and submitting the message. Set cancel to - * true if you wish to completely cancel the meeting request. You can - * specify an optional 'prefix' to prefix the sent message, which is normally - * 'Canceled: ' - */ - function sendMeetingRequest($cancel, $prefix = false, $basedate = false, $deletedRecips = false) - { - $this->includesResources = false; - $this->nonAcceptingResources = Array(); - - // Get the properties of the message - $messageprops = mapi_getprops($this->message, Array($this->proptags['recurring'])); - - /***************************************************************************************** - * Submit message to non-resource recipients - */ - // Set BusyStatus to olTentative (1) - // Set MeetingStatus to olMeetingReceived - // Set ResponseStatus to olResponseNotResponded - - /** - * While sending recurrence meeting exceptions are not send as attachments - * because first all exceptions are send and then recurrence meeting is sent. - */ - if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']] && !$basedate) { - // Book resource - $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix); - - if (!$this->errorSetResource) { - $recurr = new Recurrence($this->openDefaultStore(), $this->message); - - // First send meetingrequest for recurring item - $this->submitMeetingRequest($this->message, $cancel, $prefix, false, $recurr, false, $deletedRecips); - - // Then send all meeting request for all exceptions - $exceptions = $recurr->getAllExceptions(); - if ($exceptions) { - foreach($exceptions as $exceptionBasedate) { - $attach = $recurr->getExceptionAttachment($exceptionBasedate); - - if ($attach) { - $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY); - $this->submitMeetingRequest($occurrenceItem, $cancel, false, $exceptionBasedate, $recurr, false, $deletedRecips); - mapi_savechanges($attach); - } - } - } - } - } else { - // Basedate found, an exception is to be send - if ($basedate) { - $recurr = new Recurrence($this->openDefaultStore(), $this->message); - - if ($cancel) { - //@TODO: remove occurrence from Resource's Calendar if resource was booked for whole series - $this->submitMeetingRequest($this->message, $cancel, $prefix, $basedate, $recurr, false); - } else { - $attach = $recurr->getExceptionAttachment($basedate); - - if ($attach) { - $occurrenceItem = mapi_attach_openobj($attach, MAPI_MODIFY); - - // Book resource for this occurrence - $resourceRecipData = $this->bookResources($occurrenceItem, $cancel, $prefix, $basedate); - - if (!$this->errorSetResource) { - // Save all previous changes - mapi_savechanges($this->message); - - $this->submitMeetingRequest($occurrenceItem, $cancel, $prefix, $basedate, $recurr, true, $deletedRecips); - mapi_savechanges($occurrenceItem); - mapi_savechanges($attach); - } - } - } - } else { - // This is normal meeting - $resourceRecipData = $this->bookResources($this->message, $cancel, $prefix); - - if (!$this->errorSetResource) { - $this->submitMeetingRequest($this->message, $cancel, $prefix, false, false, false, $deletedRecips); - } - } - } - - if(isset($this->errorSetResource) && $this->errorSetResource){ - return Array( - 'error' => $this->errorSetResource, - 'displayname' => $this->recipientDisplayname - ); - }else{ - return true; - } - } - - - function getFreeBusyInfo($entryID,$start,$end) - { - $result = array(); - $fbsupport = mapi_freebusysupport_open($this->session); - - if(mapi_last_hresult() != NOERROR) { - if(function_exists("dump")) { - dump("Error in opening freebusysupport object."); - } - return $result; - } - - $fbDataArray = mapi_freebusysupport_loaddata($fbsupport, array($entryID)); - - if($fbDataArray[0] != NULL){ - foreach($fbDataArray as $fbDataUser){ - $rangeuser1 = mapi_freebusydata_getpublishrange($fbDataUser); - if($rangeuser1 == NULL){ - return $result; - } - - $enumblock = mapi_freebusydata_enumblocks($fbDataUser, $start, $end); - mapi_freebusyenumblock_reset($enumblock); - - while(true){ - $blocks = mapi_freebusyenumblock_next($enumblock, 100); - if(!$blocks){ - break; - } - foreach($blocks as $blockItem){ - $result[] = $blockItem; - } - } - } - } - - mapi_freebusysupport_close($fbsupport); - return $result; - } - - /** - * Updates the message after an update has been performed (for example, - * changing the time of the meeting). This must be called before re-sending - * the meeting request. You can also call this function instead of 'setMeetingRequest()' - * as it will automatically call setMeetingRequest on this object if it is the first - * call to this function. - */ - function updateMeetingRequest($basedate = false) - { - $messageprops = mapi_getprops($this->message, Array($this->proptags['last_updatecounter'], $this->proptags['goid'])); - - if(!isset($messageprops[$this->proptags['last_updatecounter']]) || !isset($messageprops[$this->proptags['goid']])) { - $this->setMeetingRequest($basedate); - } else { - $counter = $messageprops[$this->proptags['last_updatecounter']] + 1; - - // increment value of last_updatecounter, last_updatecounter will be common for recurring series - // so even if you sending an exception only you need to update the last_updatecounter in the recurring series message - // this way we can make sure that everytime we will be using a uniwue number for every operation - mapi_setprops($this->message, Array($this->proptags['last_updatecounter'] => $counter)); - } - } - - /** - * Returns TRUE if we are the organiser of the meeting. - */ - function isLocalOrganiser() - { - if($this->isMeetingRequest() || $this->isMeetingRequestResponse()) { - $messageid = $this->getAppointmentEntryID(); - - if(!isset($messageid)) - return false; - - $message = mapi_msgstore_openentry($this->store, $messageid); - - $messageprops = mapi_getprops($this->message, Array($this->proptags['goid'])); - $basedate = $this->getBasedateFromGlobalID($messageprops[$this->proptags['goid']]); - if ($basedate) { - $recurr = new Recurrence($this->store, $message); - $attach = $recurr->getExceptionAttachment($basedate); - if ($attach) { - $occurItem = mapi_attach_openobj($attach); - $occurItemProps = mapi_getprops($occurItem, Array($this->proptags['responsestatus'])); - } - } - - $messageprops = mapi_getprops($message, Array($this->proptags['responsestatus'])); - } - - /** - * User can send recurring meeting or any occurrences from a recurring appointment so - * to be organizer 'responseStatus' property should be 'olResponseOrganized' on either - * of the recurring item or occurrence item. - */ - if ((isset($messageprops[$this->proptags['responsestatus']]) && $messageprops[$this->proptags['responsestatus']] == olResponseOrganized) - || (isset($occurItemProps[$this->proptags['responsestatus']]) && $occurItemProps[$this->proptags['responsestatus']] == olResponseOrganized)) - return true; - else - return false; - } - - /** - * Returns the entryid of the appointment that this message points at. This is - * only used on messages that are not in the calendar. - */ - function getAppointmentEntryID() - { - $messageprops = mapi_getprops($this->message, Array($this->proptags['goid2'])); - - $goid2 = $messageprops[$this->proptags['goid2']]; - - $items = $this->findCalendarItems($goid2); - - if(empty($items)) - return; - - // There should be just one item. If there are more, we just take the first one - return $items[0]; - } - - /*************************************************************************************************** - * Support functions - INTERNAL ONLY - *************************************************************************************************** - */ - - /** - * Return the tracking status of a recipient based on the IPM class (passed) - */ - function getTrackStatus($class) { - $status = olRecipientTrackStatusNone; - switch($class) - { - case "IPM.Schedule.Meeting.Resp.Pos": - $status = olRecipientTrackStatusAccepted; - break; - - case "IPM.Schedule.Meeting.Resp.Tent": - $status = olRecipientTrackStatusTentative; - break; - - case "IPM.Schedule.Meeting.Resp.Neg": - $status = olRecipientTrackStatusDeclined; - break; - } - return $status; - } - - function openParentFolder() { - $messageprops = mapi_getprops($this->message, Array(PR_PARENT_ENTRYID)); - - $parentfolder = mapi_msgstore_openentry($this->store, $messageprops[PR_PARENT_ENTRYID]); - return $parentfolder; - } - - function openDefaultCalendar() { - return $this->openDefaultFolder(PR_IPM_APPOINTMENT_ENTRYID); - } - - function openDefaultOutbox($store=false) { - return $this->openBaseFolder(PR_IPM_OUTBOX_ENTRYID, $store); - } - - function openDefaultWastebasket() { - return $this->openBaseFolder(PR_IPM_WASTEBASKET_ENTRYID); - } - - function getDefaultWastebasketEntryID() { - return $this->getBaseEntryID(PR_IPM_WASTEBASKET_ENTRYID); - } - - function getDefaultSentmailEntryID($store=false) { - return $this->getBaseEntryID(PR_IPM_SENTMAIL_ENTRYID, $store); - } - - function getDefaultFolderEntryID($prop) { - try { - $inbox = mapi_msgstore_getreceivefolder($this->store); - } catch (MAPIException $e) { - // public store doesn't support this method - if($e->getCode() == MAPI_E_NO_SUPPORT) { - // don't propogate this error to parent handlers, if store doesn't support it - $e->setHandled(); - return; - } - } - - $inboxprops = mapi_getprops($inbox, Array($prop)); - if(!isset($inboxprops[$prop])) - return; - - return $inboxprops[$prop]; - } - - function openDefaultFolder($prop) { - $entryid = $this->getDefaultFolderEntryID($prop); - $folder = mapi_msgstore_openentry($this->store, $entryid); - - return $folder; - } - - function getBaseEntryID($prop, $store=false) { - $storeprops = mapi_getprops( (($store)?$store:$this->store) , Array($prop)); - if(!isset($storeprops[$prop])) - return; - - return $storeprops[$prop]; - } - - function openBaseFolder($prop, $store=false) { - $entryid = $this->getBaseEntryID($prop, $store); - $folder = mapi_msgstore_openentry( (($store)?$store:$this->store) , $entryid); - - return $folder; - } - /** - * Function which sends response to organizer when attendee accepts, declines or proposes new time to a received meeting request. - *@param integer $status response status of attendee - *@param integer $proposalStartTime proposed starttime by attendee - *@param integer $proposalEndTime proposed endtime by attendee - *@param integer $basedate date of occurrence which attendee has responded - */ - function createResponse($status, $proposalStartTime=false, $proposalEndTime=false, $body=false, $store, $basedate = false, $calFolder) { - $messageprops = mapi_getprops($this->message, Array(PR_SENT_REPRESENTING_ENTRYID, - PR_SENT_REPRESENTING_EMAIL_ADDRESS, - PR_SENT_REPRESENTING_ADDRTYPE, - PR_SENT_REPRESENTING_NAME, - $this->proptags['goid'], - $this->proptags['goid2'], - $this->proptags['location'], - $this->proptags['startdate'], - $this->proptags['duedate'], - $this->proptags['recurring'], - $this->proptags['recurring_pattern'], - $this->proptags['recurrence_data'], - $this->proptags['timezone_data'], - $this->proptags['timezone'], - $this->proptags['updatecounter'], - PR_SUBJECT, - PR_MESSAGE_CLASS, - PR_OWNER_APPT_ID, - $this->proptags['is_exception'] - )); - - if ($basedate && $messageprops[PR_MESSAGE_CLASS] != "IPM.Schedule.Meeting.Request" ){ - // we are creating response from a recurring calendar item object - // We found basedate,so opened occurrence and get properties. - $recurr = new Recurrence($store, $this->message); - $exception = $recurr->getExceptionAttachment($basedate); - - if ($exception) { - // Exception found, Now retrieve properties - $imessage = mapi_attach_openobj($exception, 0); - $imsgprops = mapi_getprops($imessage); - - // If location is provided, copy it to the response - if (isset($imsgprops[$this->proptags['location']])) { - $messageprops[$this->proptags['location']] = $imsgprops[$this->proptags['location']]; - } - - // Update $messageprops with timings of occurrence - $messageprops[$this->proptags['startdate']] = $imsgprops[$this->proptags['startdate']]; - $messageprops[$this->proptags['duedate']] = $imsgprops[$this->proptags['duedate']]; - - // Meeting related properties - $props[$this->proptags['meetingstatus']] = $imsgprops[$this->proptags['meetingstatus']]; - $props[$this->proptags['responsestatus']] = $imsgprops[$this->proptags['responsestatus']]; - $props[PR_SUBJECT] = $imsgprops[PR_SUBJECT]; - } else { - // Exceptions is deleted. - // Update $messageprops with timings of occurrence - $messageprops[$this->proptags['startdate']] = $recurr->getOccurrenceStart($basedate); - $messageprops[$this->proptags['duedate']] = $recurr->getOccurrenceEnd($basedate); - - $props[$this->proptags['meetingstatus']] = olNonMeeting; - $props[$this->proptags['responsestatus']] = olResponseNone; - } - - $props[$this->proptags['recurring']] = false; - $props[$this->proptags['is_exception']] = true; - } else { - // we are creating a response from meeting request mail (it could be recurring or non-recurring) - // Send all recurrence info in response, if this is a recurrence meeting. - $isRecurring = isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]; - $isException = isset($messageprops[$this->proptags['is_exception']]) && $messageprops[$this->proptags['is_exception']]; - if ($isRecurring || $isException) { - if($isRecurring) { - $props[$this->proptags['recurring']] = $messageprops[$this->proptags['recurring']]; - } - if($isException) { - $props[$this->proptags['is_exception']] = $messageprops[$this->proptags['is_exception']]; - } - $calendaritems = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder); - - $calendaritem = mapi_msgstore_openentry($this->store, $calendaritems[0]); - $recurr = new Recurrence($store, $calendaritem); - } - } - - // we are sending a response for recurring meeting request (or exception), so set some required properties - if(isset($recurr) && $recurr) { - if(!empty($messageprops[$this->proptags['recurring_pattern']])) { - $props[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']]; - } - - if(!empty($messageprops[$this->proptags['recurrence_data']])) { - $props[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']]; - } - - $props[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']]; - $props[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']]; - - $this->generateRecurDates($recurr, $messageprops, $props); - } - - // Create a response message - $recip = Array(); - $recip[PR_ENTRYID] = $messageprops[PR_SENT_REPRESENTING_ENTRYID]; - $recip[PR_EMAIL_ADDRESS] = $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS]; - $recip[PR_ADDRTYPE] = $messageprops[PR_SENT_REPRESENTING_ADDRTYPE]; - $recip[PR_DISPLAY_NAME] = $messageprops[PR_SENT_REPRESENTING_NAME]; - $recip[PR_RECIPIENT_TYPE] = MAPI_TO; - - switch($status) { - case olResponseAccepted: - $classpostfix = "Pos"; - $subjectprefix = _("Accepted"); - break; - case olResponseDeclined: - $classpostfix = "Neg"; - $subjectprefix = _("Declined"); - break; - case olResponseTentative: - $classpostfix = "Tent"; - $subjectprefix = _("Tentatively accepted"); - break; - } - - if($proposalStartTime && $proposalEndTime){ - // if attendee has proposed new time then change subject prefix - $subjectprefix = _("New Time Proposed"); - } - - $props[PR_SUBJECT] = $subjectprefix . ": " . $messageprops[PR_SUBJECT]; - - $props[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Resp." . $classpostfix; - if(isset($messageprops[PR_OWNER_APPT_ID])) - $props[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID]; - - // Set GLOBALID AND CLEANGLOBALID, if exception then also set basedate into GLOBALID(0x3). - $props[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate); - $props[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']]; - $props[$this->proptags['updatecounter']] = $messageprops[$this->proptags['updatecounter']]; - - // get the default store, in which we have to store the accepted email by delegate or normal user. - $defaultStore = $this->openDefaultStore(); - $props[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($defaultStore); - - if($proposalStartTime && $proposalEndTime){ - $props[$this->proptags['proposed_start_whole']] = $proposalStartTime; - $props[$this->proptags['proposed_end_whole']] = $proposalEndTime; - $props[$this->proptags['proposed_duration']] = round($proposalEndTime - $proposalStartTime)/60; - $props[$this->proptags['counter_proposal']] = true; - } - - //Set body message in Appointment - if(isset($body)) { - $props[PR_BODY] = $this->getMeetingTimeInfo() ? $this->getMeetingTimeInfo() : $body; - } - - // PR_START_DATE/PR_END_DATE is used in the UI in Outlook on the response message - $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']]; - $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']]; - - // Set startdate and duedate in response mail. - $props[$this->proptags['startdate']] = $messageprops[$this->proptags['startdate']]; - $props[$this->proptags['duedate']] = $messageprops[$this->proptags['duedate']]; - - // responselocation is used in the UI in Outlook on the response message - if (isset($messageprops[$this->proptags['location']])) { - $props[$this->proptags['responselocation']] = $messageprops[$this->proptags['location']]; - $props[$this->proptags['location']] = $messageprops[$this->proptags['location']]; - } - - // check if $store is set and it is not equal to $defaultStore (means its the delegation case) - if(isset($store) && isset($defaultStore)) { - $storeProps = mapi_getprops($store, array(PR_ENTRYID)); - $defaultStoreProps = mapi_getprops($defaultStore, array(PR_ENTRYID)); - - if($storeProps[PR_ENTRYID] !== $defaultStoreProps[PR_ENTRYID]){ - // get the properties of the other user (for which the logged in user is a delegate). - $storeProps = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID)); - $addrbook = mapi_openaddressbook($this->session); - $addrbookitem = mapi_ab_openentry($addrbook, $storeProps[PR_MAILBOX_OWNER_ENTRYID]); - $addrbookitemprops = mapi_getprops($addrbookitem, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS)); - - // setting the following properties will ensure that the delegation part of message. - $props[PR_SENT_REPRESENTING_ENTRYID] = $storeProps[PR_MAILBOX_OWNER_ENTRYID]; - $props[PR_SENT_REPRESENTING_NAME] = $addrbookitemprops[PR_DISPLAY_NAME]; - $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $addrbookitemprops[PR_EMAIL_ADDRESS]; - $props[PR_SENT_REPRESENTING_ADDRTYPE] = "ZARAFA"; - - // get the properties of default store and set it accordingly - $defaultStoreProps = mapi_getprops($defaultStore, array(PR_MAILBOX_OWNER_ENTRYID)); - $addrbookitem = mapi_ab_openentry($addrbook, $defaultStoreProps[PR_MAILBOX_OWNER_ENTRYID]); - $addrbookitemprops = mapi_getprops($addrbookitem, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS)); - - // set the following properties will ensure the sender's details, which will be the default user in this case. - //the function returns array($name, $emailaddr, $addrtype, $entryid, $searchkey); - $defaultUserDetails = $this->getOwnerAddress($defaultStore); - $props[PR_SENDER_ENTRYID] = $defaultUserDetails[3]; - $props[PR_SENDER_EMAIL_ADDRESS] = $defaultUserDetails[1]; - $props[PR_SENDER_NAME] = $defaultUserDetails[0]; - $props[PR_SENDER_ADDRTYPE] = $defaultUserDetails[2]; - } - } - - // pass the default store to get the required store. - $outbox = $this->openDefaultOutbox($defaultStore); - - $message = mapi_folder_createmessage($outbox); - mapi_setprops($message, $props); - mapi_message_modifyrecipients($message, MODRECIP_ADD, Array($recip)); - mapi_message_savechanges($message); - mapi_message_submitmessage($message); - } - - /** - * Function which finds items in calendar based on specified parameters. - *@param binary $goid GlobalID(0x3) of item - *@param resource $calendar MAPI_folder of user - *@param boolean $use_cleanGlobalID if true then search should be performed on cleanGlobalID(0x23) else globalID(0x3) - */ - function findCalendarItems($goid, $calendar = false, $use_cleanGlobalID = false) { - if(!$calendar) { - // Open the Calendar - $calendar = $this->openDefaultCalendar(); - } - - // Find the item by restricting all items to the correct ID - $restrict = Array(RES_AND, Array()); - - array_push($restrict[1], Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => ($use_cleanGlobalID ? $this->proptags['goid2'] : $this->proptags['goid']), - VALUE => $goid - ) - )); - - $calendarcontents = mapi_folder_getcontentstable($calendar); - - $rows = mapi_table_queryallrows($calendarcontents, Array(PR_ENTRYID), $restrict); - - if(empty($rows)) - return; - - $calendaritems = Array(); - - // In principle, there should only be one row, but we'll handle them all just in case - foreach($rows as $row) { - $calendaritems[] = $row[PR_ENTRYID]; - } - - return $calendaritems; - } - - // Returns TRUE if both entryid's are equal. Equality is defined by both entryid's pointing at the - // same SMTP address when converted to SMTP - function compareABEntryIDs($entryid1, $entryid2) { - // If the session was not passed, just do a 'normal' compare. - if(!$this->session) - return $entryid1 == $entryid2; - - $smtp1 = $this->getSMTPAddress($entryid1); - $smtp2 = $this->getSMTPAddress($entryid2); - - if($smtp1 == $smtp2) - return true; - else - return false; - } - - // Gets the SMTP address of the passed addressbook entryid - function getSMTPAddress($entryid) { - if(!$this->session) - return false; - - $ab = mapi_openaddressbook($this->session); - - $abitem = mapi_ab_openentry($ab, $entryid); - - if(!$abitem) - return ""; - - $props = mapi_getprops($abitem, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS)); - - if($props[PR_ADDRTYPE] == "SMTP") { - return $props[PR_EMAIL_ADDRESS]; - } - else return $props[PR_SMTP_ADDRESS]; - } - - /** - * Gets the properties associated with the owner of the passed store: - * PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID, PR_SEARCH_KEY - * - * @param $store message store - * @param $fallbackToLoggedInUser if true then return properties of logged in user instead of mailbox owner - * not used when passed store is public store. for public store we are always returning logged in user's info. - * @return properties of logged in user in an array in sequence of display_name, email address, address tyep, - * entryid and search key. - */ - function getOwnerAddress($store, $fallbackToLoggedInUser = true) - { - if(!$this->session) - return false; - - $storeProps = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID, PR_USER_ENTRYID)); - - $ownerEntryId = false; - if(isset($storeProps[PR_USER_ENTRYID]) && $storeProps[PR_USER_ENTRYID]) { - $ownerEntryId = $storeProps[PR_USER_ENTRYID]; - } - - if(isset($storeProps[PR_MAILBOX_OWNER_ENTRYID]) && $storeProps[PR_MAILBOX_OWNER_ENTRYID] && !$fallbackToLoggedInUser) { - $ownerEntryId = $storeProps[PR_MAILBOX_OWNER_ENTRYID]; - } - - if($ownerEntryId) { - $ab = mapi_openaddressbook($this->session); - - $zarafaUser = mapi_ab_openentry($ab, $ownerEntryId); - if(!$zarafaUser) - return false; - - $ownerProps = mapi_getprops($zarafaUser, array(PR_ADDRTYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS)); - - $addrType = $ownerProps[PR_ADDRTYPE]; - $name = $ownerProps[PR_DISPLAY_NAME]; - $emailAddr = $ownerProps[PR_EMAIL_ADDRESS]; - $searchKey = strtoupper($addrType) . ":" . strtoupper($emailAddr); - $entryId = $ownerEntryId; - - return array($name, $emailAddr, $addrType, $entryId, $searchKey); - } - - return false; - } - - // Opens this session's default message store - function openDefaultStore() - { - $storestable = mapi_getmsgstorestable($this->session); - $rows = mapi_table_queryallrows($storestable, array(PR_ENTRYID, PR_DEFAULT_STORE)); - $entry = false; - - foreach($rows as $row) { - if(isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE]) { - $entryid = $row[PR_ENTRYID]; - break; - } - } - - if(!$entryid) - return false; - - return mapi_openmsgstore($this->session, $entryid); - } - /** - * Function which adds organizer to recipient list which is passed. - * This function also checks if it has organizer. - * - * @param array $messageProps message properties - * @param array $recipients recipients list of message. - * @param boolean $isException true if we are processing recipient of exception - */ - function addOrganizer($messageProps, &$recipients, $isException = false){ - - $hasOrganizer = false; - // Check if meeting already has an organizer. - foreach ($recipients as $key => $recipient){ - if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) { - $hasOrganizer = true; - } else if ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])){ - // Recipients for an occurrence - $recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse; - } - } - - if (!$hasOrganizer){ - // Create organizer. - $organizer = array(); - $organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID]; - $organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME]; - $organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS]; - $organizer[PR_RECIPIENT_TYPE] = MAPI_TO; - $organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME]; - $organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP':$messageProps[PR_SENT_REPRESENTING_ADDRTYPE]; - $organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; - $organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer; - - // Add organizer to recipients list. - array_unshift($recipients, $organizer); - } - } - - /** - * Function adds recipients in recips array from the string. - * - * @param array $recips recipient array. - * @param string $recipString recipient string attendees. - * @param int $type type of the recipient, MAPI_TO/MAPI_CC. - */ - function setRecipsFromString(&$recips, $recipString, $recipType = MAPI_TO) - { - $extraRecipient = array(); - $recipArray = explode(";", $recipString); - - foreach($recipArray as $recip) { - $recip = trim($recip); - if (!empty($recip)) { - $extraRecipient[PR_RECIPIENT_TYPE] = $recipType; - $extraRecipient[PR_DISPLAY_NAME] = $recip; - array_push($recips, $extraRecipient); - } - } - - } - - /** - * Function which removes an exception/occurrence from recurrencing meeting - * when a meeting cancellation of an occurrence is processed. - *@param string $basedate basedate of an occurrence - *@param resource $message recurring item from which occurrence has to be deleted - *@param resource $store MAPI_MSG_Store which contains the item - */ - function doRemoveExceptionFromCalendar($basedate, $message, $store) - { - $recurr = new Recurrence($store, $message); - $recurr->createException(array(), $basedate, true); - mapi_savechanges($message); - } - - /** - * Function which returns basedate of an changed occurrance from globalID of meeting request. - *@param binary $goid globalID - *@return boolean true if basedate is found else false it not found - */ - function getBasedateFromGlobalID($goid) - { - $hexguid = bin2hex($goid); - $hexbase = substr($hexguid, 32, 8); - $day = hexdec(substr($hexbase, 6, 2)); - $month = hexdec(substr($hexbase, 4, 2)); - $year = hexdec(substr($hexbase, 0, 4)); - - if ($day && $month && $year) - return gmmktime(0, 0, 0, $month, $day, $year); - else - return false; - } - - /** - * Function which sets basedate in globalID of changed occurrance which is to be send. - *@param binary $goid globalID - *@param string basedate of changed occurrance - *@return binary globalID with basedate in it - */ - function setBasedateInGlobalID($goid, $basedate = false) - { - $hexguid = bin2hex($goid); - $year = $basedate ? sprintf('%04s', dechex(date('Y', $basedate))) : '0000'; - $month = $basedate ? sprintf('%02s', dechex(date('m', $basedate))) : '00'; - $day = $basedate ? sprintf('%02s', dechex(date('d', $basedate))) : '00'; - - return hex2bin(strtoupper(substr($hexguid, 0, 32) . $year . $month . $day . substr($hexguid, 40))); - } - /** - * Function which replaces attachments with copy_from in copy_to. - *@param resource $copy_from MAPI_message from which attachments are to be copied. - *@param resource $copy_to MAPI_message to which attachment are to be copied. - *@param boolean $copyExceptions if true then all exceptions should also be sent as attachments - */ - function replaceAttachments($copy_from, $copy_to, $copyExceptions = true) - { - /* remove all old attachments */ - $attachmentTable = mapi_message_getattachmenttable($copy_to); - if($attachmentTable) { - $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME)); - - foreach($attachments as $attach_props){ - /* remove exceptions too? */ - if (!$copyExceptions && $attach_props[PR_ATTACH_METHOD] == 5 && isset($attach_props[PR_EXCEPTION_STARTTIME])) - continue; - mapi_message_deleteattach($copy_to, $attach_props[PR_ATTACH_NUM]); - } - } - $attachmentTable = false; - - /* copy new attachments */ - $attachmentTable = mapi_message_getattachmenttable($copy_from); - if($attachmentTable) { - $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_METHOD, PR_EXCEPTION_STARTTIME)); - - foreach($attachments as $attach_props){ - if (!$copyExceptions && $attach_props[PR_ATTACH_METHOD] == 5 && isset($attach_props[PR_EXCEPTION_STARTTIME])) - continue; - - $attach_old = mapi_message_openattach($copy_from, (int) $attach_props[PR_ATTACH_NUM]); - $attach_newResourceMsg = mapi_message_createattach($copy_to); - mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0); - mapi_savechanges($attach_newResourceMsg); - } - } - } - /** - * Function which replaces recipients in copy_to with recipients from copy_from. - *@param resource $copy_from MAPI_message from which recipients are to be copied. - *@param resource $copy_to MAPI_message to which recipients are to be copied. - */ - function replaceRecipients($copy_from, $copy_to, $isDelegate = false) - { - $recipienttable = mapi_message_getrecipienttable($copy_from); - - // If delegate, then do not add the delegate in recipients - if ($isDelegate) { - $delegate = mapi_getprops($copy_from, array(PR_RECEIVED_BY_EMAIL_ADDRESS)); - $res = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => PR_EMAIL_ADDRESS, VALUE => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS])); - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $res); - } else { - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops); - } - - $copy_to_recipientTable = mapi_message_getrecipienttable($copy_to); - $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID)); - foreach($copy_to_recipientRows as $recipient) { - mapi_message_modifyrecipients($copy_to, MODRECIP_REMOVE, array($recipient)); - } - - mapi_message_modifyrecipients($copy_to, MODRECIP_ADD, $recipients); - } - /** - * Function creates meeting item in resource's calendar. - *@param resource $message MAPI_message which is to create in resource's calendar - *@param boolean $cancel cancel meeting - *@param string $prefix prefix for subject of meeting - */ - function bookResources($message, $cancel, $prefix, $basedate = false) - { - if(!$this->enableDirectBooking) - return array(); - - // Get the properties of the message - $messageprops = mapi_getprops($message); - - if ($basedate) { - $recurrItemProps = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['timezone_data'], $this->proptags['timezone'], PR_OWNER_APPT_ID)); - - $messageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($recurrItemProps[$this->proptags['goid']], $basedate); - $messageprops[$this->proptags['goid2']] = $recurrItemProps[$this->proptags['goid2']]; - - // Delete properties which are not needed. - $deleteProps = array($this->proptags['basedate'], PR_DISPLAY_NAME, PR_ATTACHMENT_FLAGS, PR_ATTACHMENT_HIDDEN, PR_ATTACHMENT_LINKID, PR_ATTACH_FLAGS, PR_ATTACH_METHOD); - foreach ($deleteProps as $propID) { - if (isset($messageprops[$propID])) { - unset($messageprops[$propID]); - } - } - - if (isset($messageprops[$this->proptags['recurring']])) $messageprops[$this->proptags['recurring']] = false; - - // Set Outlook properties - $messageprops[$this->proptags['clipstart']] = $messageprops[$this->proptags['startdate']]; - $messageprops[$this->proptags['clipend']] = $messageprops[$this->proptags['duedate']]; - $messageprops[$this->proptags['timezone_data']] = $recurrItemProps[$this->proptags['timezone_data']]; - $messageprops[$this->proptags['timezone']] = $recurrItemProps[$this->proptags['timezone']]; - $messageprops[$this->proptags['attendee_critical_change']] = time(); - $messageprops[$this->proptags['owner_critical_change']] = time(); - } - - // Get resource recipients - $getResourcesRestriction = Array(RES_AND, - Array(Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, // Equals recipient type 3: Resource - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => array(PR_RECIPIENT_TYPE =>MAPI_BCC) - ) - )) - ); - $recipienttable = mapi_message_getrecipienttable($message); - $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction); - - $this->errorSetResource = false; - $resourceRecipData = Array(); - - // Put appointment into store resource users - $i = 0; - $len = count($resourceRecipients); - while(!$this->errorSetResource && $i < $len){ - $request = array(array(PR_DISPLAY_NAME => $resourceRecipients[$i][PR_DISPLAY_NAME])); - $ab = mapi_openaddressbook($this->session); - $ret = mapi_ab_resolvename($ab, $request, EMS_AB_ADDRESS_LOOKUP); - $result = mapi_last_hresult(); - if ($result == NOERROR){ - $result = $ret[0][PR_ENTRYID]; - } - $resourceUsername = $ret[0][PR_EMAIL_ADDRESS]; - $resourceABEntryID = $ret[0][PR_ENTRYID]; - - // Get StoreEntryID by username - $user_entryid = mapi_msgstore_createentryid($this->store, $resourceUsername); - - // Open store of the user - $userStore = mapi_openmsgstore($this->session, $user_entryid); - // Open root folder - $userRoot = mapi_msgstore_openentry($userStore, null); - // Get calendar entryID - $userRootProps = mapi_getprops($userRoot, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS)); - - // Open Calendar folder [check hresult==0] - $accessToFolder = false; - try { - $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]); - if($calFolder){ - $calFolderProps = mapi_getProps($calFolder, Array(PR_ACCESS)); - if(($calFolderProps[PR_ACCESS] & MAPI_ACCESS_CREATE_CONTENTS) !== 0){ - $accessToFolder = true; - } - } - } catch (MAPIException $e) { - $e->setHandled(); - $this->errorSetResource = 1; // No access - } - - if($accessToFolder) { - /** - * Get the LocalFreebusy message that contains the properties that - * are set to accept or decline resource meeting requests - */ - // Use PR_FREEBUSY_ENTRYIDS[1] to open folder the LocalFreeBusy msg - $localFreebusyMsg = mapi_msgstore_openentry($userStore, $userRootProps[PR_FREEBUSY_ENTRYIDS][1]); - if($localFreebusyMsg){ - $props = mapi_getprops($localFreebusyMsg, array(PR_PROCESS_MEETING_REQUESTS, PR_DECLINE_RECURRING_MEETING_REQUESTS, PR_DECLINE_CONFLICTING_MEETING_REQUESTS)); - - $acceptMeetingRequests = ($props[PR_PROCESS_MEETING_REQUESTS])?1:0; - $declineRecurringMeetingRequests = ($props[PR_DECLINE_RECURRING_MEETING_REQUESTS])?1:0; - $declineConflictingMeetingRequests = ($props[PR_DECLINE_CONFLICTING_MEETING_REQUESTS])?1:0; - if(!$acceptMeetingRequests){ - /** - * When a resource has not been set to automatically accept meeting requests, - * the meeting request has to be sent to him rather than being put directly into - * his calendar. No error should be returned. - */ - //$errorSetResource = 2; - $this->nonAcceptingResources[] = $resourceRecipients[$i]; - }else{ - if($declineRecurringMeetingRequests && !$cancel){ - // Check if appointment is recurring - if($messageprops[ $this->proptags['recurring'] ]){ - $this->errorSetResource = 3; - } - } - if($declineConflictingMeetingRequests && !$cancel){ - // Check for conflicting items - $conflicting = false; - - // Open the calendar - $calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]); - - if($calFolder) { - if ($this->isMeetingConflicting($message, $userStore, $calFolder, $messageprops)) - $conflicting = true; - } else { - $this->errorSetResource = 1; // No access - } - - if($conflicting){ - $this->errorSetResource = 4; // Conflict - } - } - } - } - } - - if(!$this->errorSetResource && $accessToFolder){ - /** - * First search on GlobalID(0x3) - * If (recurring and occurrence) If Resource was booked for only this occurrence then Resource should have only this occurrence in Calendar and not whole series. - * If (normal meeting) then GlobalID(0x3) and CleanGlobalID(0x23) are same, so doesnt matter if search is based on GlobalID. - */ - $rows = $this->findCalendarItems($messageprops[$this->proptags['goid']], $calFolder); - - /** - * If no entry is found then - * 1) Resource doesnt have meeting in Calendar. Seriously!! - * OR - * 2) We were looking for occurrence item but Resource has whole series - */ - if(empty($rows)){ - /** - * Now search on CleanGlobalID(0x23) WHY??? - * Because we are looking recurring item - * - * Possible results of this search - * 1) If Resource was booked for more than one occurrences then this search will return all those occurrence because search is perform on CleanGlobalID - * 2) If Resource was booked for whole series then it should return series. - */ - $rows = $this->findCalendarItems($messageprops[$this->proptags['goid2']], $calFolder, true); - - $newResourceMsg = false; - if (!empty($rows)) { - // Since we are looking for recurring item, open every result and check for 'recurring' property. - foreach($rows as $row) { - $ResourceMsg = mapi_msgstore_openentry($userStore, $row); - $ResourceMsgProps = mapi_getprops($ResourceMsg, array($this->proptags['recurring'])); - - if (isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) { - $newResourceMsg = $ResourceMsg; - break; - } - } - } - - // Still no results found. I giveup, create new message. - if (!$newResourceMsg) - $newResourceMsg = mapi_folder_createmessage($calFolder); - }else{ - $newResourceMsg = mapi_msgstore_openentry($userStore, $rows[0]); - } - - // Prefix the subject if needed - if($prefix && isset($messageprops[PR_SUBJECT])) { - $messageprops[PR_SUBJECT] = $prefix . $messageprops[PR_SUBJECT]; - } - - // Set status to cancelled if needed - $messageprops[$this->proptags['busystatus']] = fbBusy; // The default status (Busy) - if($cancel) { - $messageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // The meeting has been canceled - $messageprops[$this->proptags['busystatus']] = fbFree; // Free - } else { - $messageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request - } - $messageprops[$this->proptags['responsestatus']] = olResponseAccepted; // The resource autmatically accepts the appointment - - $messageprops[PR_MESSAGE_CLASS] = "IPM.Appointment"; - - // Remove the PR_ICON_INDEX as it is not needed in the sent message and it also - // confuses the Zarafa webaccess - $messageprops[PR_ICON_INDEX] = null; - $messageprops[PR_RESPONSE_REQUESTED] = true; - - $addrinfo = $this->getOwnerAddress($this->store); - - if($addrinfo) { - list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo; - - $messageprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr; - $messageprops[PR_SENT_REPRESENTING_NAME] = $ownername; - $messageprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype; - $messageprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid; - $messageprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey; - - $messageprops[$this->proptags['apptreplyname']] = $ownername; - $messageprops[$this->proptags['replytime']] = time(); - } - - if ($basedate && isset($ResourceMsgProps[$this->proptags['recurring']]) && $ResourceMsgProps[$this->proptags['recurring']]) { - $recurr = new Recurrence($userStore, $newResourceMsg); - - // Copy recipients list - $reciptable = mapi_message_getrecipienttable($message); - $recips = mapi_table_queryallrows($reciptable, $this->recipprops); - // add owner to recipient table - $this->addOrganizer($messageprops, $recips, true); - - // Update occurrence - if($recurr->isException($basedate)) - $recurr->modifyException($messageprops, $basedate, $recips); - else - $recurr->createException($messageprops, $basedate, false, $recips); - } else { - - mapi_setprops($newResourceMsg, $messageprops); - - // Copy attachments - $this->replaceAttachments($message, $newResourceMsg); - - // Copy all recipients too - $this->replaceRecipients($message, $newResourceMsg); - - // Now add organizer also to recipient table - $recips = Array(); - $this->addOrganizer($messageprops, $recips); - mapi_message_modifyrecipients($newResourceMsg, MODRECIP_ADD, $recips); - } - - mapi_savechanges($newResourceMsg); - - $resourceRecipData[] = Array( - 'store' => $userStore, - 'folder' => $calFolder, - 'msg' => $newResourceMsg, - ); - $this->includesResources = true; - }else{ - /** - * If no other errors occured and you have no access to the - * folder of the resource, throw an error=1. - */ - if(!$this->errorSetResource){ - $this->errorSetResource = 1; - } - - for($j = 0, $len = count($resourceRecipData); $j < $len; $j++){ - // Get the EntryID - $props = mapi_message_getprops($resourceRecipData[$j]['msg']); - - mapi_folder_deletemessages($resourceRecipData[$j]['folder'], Array($props[PR_ENTRYID]), DELETE_HARD_DELETE); - } - $this->recipientDisplayname = $resourceRecipients[$i][PR_DISPLAY_NAME]; - } - $i++; - } - - /************************************************************** - * Set the BCC-recipients (resources) tackstatus to accepted. - */ - // Get resource recipients - $getResourcesRestriction = Array(RES_AND, - Array(Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, // Equals recipient type 3: Resource - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => array(PR_RECIPIENT_TYPE =>MAPI_BCC) - ) - )) - ); - $recipienttable = mapi_message_getrecipienttable($message); - $resourceRecipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $getResourcesRestriction); - if(!empty($resourceRecipients)){ - // Set Tracking status of resource recipients to olResponseAccepted (3) - for($i = 0, $len = count($resourceRecipients); $i < $len; $i++){ - $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusAccepted; - $resourceRecipients[$i][PR_RECIPIENT_TRACKSTATUS_TIME] = time(); - } - mapi_message_modifyrecipients($message, MODRECIP_MODIFY, $resourceRecipients); - } - - // Publish updated free/busy information - if(!$this->errorSetResource){ - for($i = 0, $len = count($resourceRecipData); $i < $len; $i++){ - $storeProps = mapi_msgstore_getprops($resourceRecipData[$i]['store'], array(PR_MAILBOX_OWNER_ENTRYID)); - if (isset($storeProps[PR_MAILBOX_OWNER_ENTRYID])){ - $pub = new FreeBusyPublish($this->session, $resourceRecipData[$i]['store'], $resourceRecipData[$i]['folder'], $storeProps[PR_MAILBOX_OWNER_ENTRYID]); - $pub->publishFB(time() - (7 * 24 * 60 * 60), 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead - } - } - } - - return $resourceRecipData; - } - /** - * Function which save an exception into recurring item - * - * @param resource $recurringItem reference to MAPI_message of recurring item - * @param resource $occurrenceItem reference to MAPI_message of occurrence - * @param string $basedate basedate of occurrence - * @param boolean $move if true then occurrence item is deleted - * @param boolean $tentative true if user has tentatively accepted it or false if user has accepted it. - * @param boolean $userAction true if user has manually responded to meeting request - * @param resource $store user store - * @param boolean $isDelegate true if delegate is processing this meeting request - */ - function acceptException(&$recurringItem, &$occurrenceItem, $basedate, $move = false, $tentative, $userAction = false, $store, $isDelegate = false) - { - $recurr = new Recurrence($store, $recurringItem); - - // Copy properties from meeting request - $exception_props = mapi_getprops($occurrenceItem); - - // Copy recipients list - $reciptable = mapi_message_getrecipienttable($occurrenceItem); - // If delegate, then do not add the delegate in recipients - if ($isDelegate) { - $delegate = mapi_getprops($this->message, array(PR_RECEIVED_BY_EMAIL_ADDRESS)); - $res = array(RES_PROPERTY, array(RELOP => RELOP_NE, ULPROPTAG => PR_EMAIL_ADDRESS, VALUE => $delegate[PR_RECEIVED_BY_EMAIL_ADDRESS])); - $recips = mapi_table_queryallrows($reciptable, $this->recipprops, $res); - } else { - $recips = mapi_table_queryallrows($reciptable, $this->recipprops); - } - - - // add owner to recipient table - $this->addOrganizer($exception_props, $recips, true); - - // add delegator to meetings - if ($isDelegate) $this->addDelegator($exception_props, $recips); - - $exception_props[$this->proptags['meetingstatus']] = olMeetingReceived; - $exception_props[$this->proptags['responsestatus']] = $userAction ? ($tentative ? olResponseTentative : olResponseAccepted) : olResponseNotResponded; - // Set basedate property (ExceptionReplaceTime) - - if (isset($exception_props[$this->proptags['intendedbusystatus']])) { - if($tentative && $exception_props[$this->proptags['intendedbusystatus']] !== fbFree) { - $exception_props[$this->proptags['busystatus']] = $tentative; - } else { - $exception_props[$this->proptags['busystatus']] = $exception_props[$this->proptags['intendedbusystatus']]; - } - // we already have intendedbusystatus value in $exception_props so no need to copy it - } else { - $exception_props[$this->proptags['busystatus']] = $tentative ? fbTentative : fbBusy; - } - - if($userAction) { - // if user has responded then set replytime - $exception_props[$this->proptags['replytime']] = time(); - } - - if($recurr->isException($basedate)) - $recurr->modifyException($exception_props, $basedate, $recips, $occurrenceItem); - else - $recurr->createException($exception_props, $basedate, false, $recips, $occurrenceItem); - - // Move the occurrenceItem to the waste basket - if ($move) { - $wastebasket = $this->openDefaultWastebasket(); - $sourcefolder = mapi_msgstore_openentry($this->store, $exception_props[PR_PARENT_ENTRYID]); - mapi_folder_copymessages($sourcefolder, Array($exception_props[PR_ENTRYID]), $wastebasket, MESSAGE_MOVE); - } - - mapi_savechanges($recurringItem); - } - - /** - * Function which submits meeting request based on arguments passed to it. - *@param resource $message MAPI_message whose meeting request is to be send - *@param boolean $cancel if true send request, else send cancellation - *@param string $prefix subject prefix - *@param integer $basedate basedate for an occurrence - *@param Object $recurObject recurrence object of mr - *@param boolean $copyExceptions When sending update mail for recurring item then we dont send exceptions in attachments - */ - function submitMeetingRequest($message, $cancel, $prefix, $basedate = false, $recurObject = false, $copyExceptions = true, $deletedRecips = false) - { - $newmessageprops = $messageprops = mapi_getprops($this->message); - $new = $this->createOutgoingMessage(); - - // Copy the entire message into the new meeting request message - if ($basedate) { - // messageprops contains properties of whole recurring series - // and newmessageprops contains properties of exception item - $newmessageprops = mapi_getprops($message); - - // Ensure that the correct basedate is set in the new message - $newmessageprops[$this->proptags['basedate']] = $basedate; - - // Set isRecurring to false, because this is an exception - $newmessageprops[$this->proptags['recurring']] = false; - - // set LID_IS_EXCEPTION to true - $newmessageprops[$this->proptags['is_exception']] = true; - - // Set to high importance - if($cancel) $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH; - - // Set startdate and enddate of exception - if ($cancel && $recurObject) { - $newmessageprops[$this->proptags['startdate']] = $recurObject->getOccurrenceStart($basedate); - $newmessageprops[$this->proptags['duedate']] = $recurObject->getOccurrenceEnd($basedate); - } - - // Set basedate in guid (0x3) - $newmessageprops[$this->proptags['goid']] = $this->setBasedateInGlobalID($messageprops[$this->proptags['goid2']], $basedate); - $newmessageprops[$this->proptags['goid2']] = $messageprops[$this->proptags['goid2']]; - $newmessageprops[PR_OWNER_APPT_ID] = $messageprops[PR_OWNER_APPT_ID]; - - // Get deleted recipiets from exception msg - $restriction = Array(RES_AND, - Array( - Array(RES_BITMASK, - Array( ULTYPE => BMR_NEZ, - ULPROPTAG => PR_RECIPIENT_FLAGS, - ULMASK => recipExceptionalDeleted - ) - ), - Array(RES_BITMASK, - Array( ULTYPE => BMR_EQZ, - ULPROPTAG => PR_RECIPIENT_FLAGS, - ULMASK => recipOrganizer - ) - ), - ) - ); - - // In direct-booking mode, we don't need to send cancellations to resources - if($this->enableDirectBooking) { - $restriction[1][] = Array(RES_PROPERTY, - Array(RELOP => RELOP_NE, // Does not equal recipient type: MAPI_BCC (Resource) - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC) - ) - ); - } - - $recipienttable = mapi_message_getrecipienttable($message); - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $restriction); - - if (!$deletedRecips) { - $deletedRecips = array_merge(array(), $recipients); - } else { - $deletedRecips = array_merge($deletedRecips, $recipients); - } - } - - // Remove the PR_ICON_INDEX as it is not needed in the sent message and it also - // confuses the Zarafa webaccess - $newmessageprops[PR_ICON_INDEX] = null; - $newmessageprops[PR_RESPONSE_REQUESTED] = true; - - // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar - $newmessageprops[PR_START_DATE] = $newmessageprops[$this->proptags['startdate']]; - $newmessageprops[PR_END_DATE] = $newmessageprops[$this->proptags['duedate']]; - - // Set updatecounter/AppointmentSequenceNumber - // get the value of latest updatecounter for the whole series and use it - $newmessageprops[$this->proptags['updatecounter']] = $messageprops[$this->proptags['last_updatecounter']]; - - $meetingTimeInfo = $this->getMeetingTimeInfo(); - - if($meetingTimeInfo) - $newmessageprops[PR_BODY] = $meetingTimeInfo; - - // Send all recurrence info in mail, if this is a recurrence meeting. - if (isset($messageprops[$this->proptags['recurring']]) && $messageprops[$this->proptags['recurring']]) { - if(!empty($messageprops[$this->proptags['recurring_pattern']])) { - $newmessageprops[$this->proptags['recurring_pattern']] = $messageprops[$this->proptags['recurring_pattern']]; - } - $newmessageprops[$this->proptags['recurrence_data']] = $messageprops[$this->proptags['recurrence_data']]; - $newmessageprops[$this->proptags['timezone_data']] = $messageprops[$this->proptags['timezone_data']]; - $newmessageprops[$this->proptags['timezone']] = $messageprops[$this->proptags['timezone']]; - - if($recurObject) { - $this->generateRecurDates($recurObject, $messageprops, $newmessageprops); - } - } - - if (isset($newmessageprops[$this->proptags['counter_proposal']])) { - unset($newmessageprops[$this->proptags['counter_proposal']]); - } - - // Prefix the subject if needed - if ($prefix && isset($newmessageprops[PR_SUBJECT])) - $newmessageprops[PR_SUBJECT] = $prefix . $newmessageprops[PR_SUBJECT]; - - mapi_setprops($new, $newmessageprops); - - // Copy attachments - $this->replaceAttachments($message, $new, $copyExceptions); - - // Retrieve only those recipient who should receive this meeting request. - $stripResourcesRestriction = Array(RES_AND, - Array( - Array(RES_BITMASK, - Array( ULTYPE => BMR_EQZ, - ULPROPTAG => PR_RECIPIENT_FLAGS, - ULMASK => recipExceptionalDeleted - ) - ), - Array(RES_BITMASK, - Array( ULTYPE => BMR_EQZ, - ULPROPTAG => PR_RECIPIENT_FLAGS, - ULMASK => recipOrganizer - ) - ), - ) - ); - - // In direct-booking mode, resources do not receive a meeting request - if($this->enableDirectBooking) { - $stripResourcesRestriction[1][] = - Array(RES_PROPERTY, - Array(RELOP => RELOP_NE, // Does not equal recipient type: MAPI_BCC (Resource) - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => array(PR_RECIPIENT_TYPE => MAPI_BCC) - ) - ); - } - - $recipienttable = mapi_message_getrecipienttable($message); - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction); - - if ($basedate && empty($recipients)) { - // Retrieve full list - $recipienttable = mapi_message_getrecipienttable($this->message); - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops); - - // Save recipients in exceptions - mapi_message_modifyrecipients($message, MODRECIP_ADD, $recipients); - - // Now retrieve only those recipient who should receive this meeting request. - $recipients = mapi_table_queryallrows($recipienttable, $this->recipprops, $stripResourcesRestriction); - } - - //@TODO: handle nonAcceptingResources - /** - * Add resource recipients that did not automatically accept the meeting request. - * (note: meaning that they did not decline the meeting request) - *//* - for($i=0;$inonAcceptingResources);$i++){ - $recipients[] = $this->nonAcceptingResources[$i]; - }*/ - - if(!empty($recipients)) { - // Strip out the sender/"owner" recipient - mapi_message_modifyrecipients($new, MODRECIP_ADD, $recipients); - - // Set some properties that are different in the sent request than - // in the item in our calendar - - // we should store busystatus value to intendedbusystatus property, because busystatus for outgoing meeting request - // should always be fbTentative - $newmessageprops[$this->proptags['intendedbusystatus']] = isset($newmessageprops[$this->proptags['busystatus']]) ? $newmessageprops[$this->proptags['busystatus']] : $messageprops[$this->proptags['busystatus']]; - $newmessageprops[$this->proptags['busystatus']] = fbTentative; // The default status when not accepted - $newmessageprops[$this->proptags['responsestatus']] = olResponseNotResponded; // The recipient has not responded yet - $newmessageprops[$this->proptags['attendee_critical_change']] = time(); - $newmessageprops[$this->proptags['owner_critical_change']] = time(); - $newmessageprops[$this->proptags['meetingtype']] = mtgRequest; - - if ($cancel) { - $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Canceled"; - $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request - $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free - } else { - $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Request"; - $newmessageprops[$this->proptags['meetingstatus']] = olMeetingReceived; // The recipient is receiving the request - } - - mapi_setprops($new, $newmessageprops); - mapi_message_savechanges($new); - - // Submit message to non-resource recipients - mapi_message_submitmessage($new); - } - - // Send cancellation to deleted attendees - if ($deletedRecips && !empty($deletedRecips)) { - $new = $this->createOutgoingMessage(); - - mapi_message_modifyrecipients($new, MODRECIP_ADD, $deletedRecips); - - $newmessageprops[PR_MESSAGE_CLASS] = "IPM.Schedule.Meeting.Canceled"; - $newmessageprops[$this->proptags['meetingstatus']] = olMeetingCanceled; // It's a cancel request - $newmessageprops[$this->proptags['busystatus']] = fbFree; // set the busy status as free - $newmessageprops[PR_IMPORTANCE] = IMPORTANCE_HIGH; // HIGH Importance - if (isset($newmessageprops[PR_SUBJECT])) { - $newmessageprops[PR_SUBJECT] = _('Canceled: ') . $newmessageprops[PR_SUBJECT]; - } - - mapi_setprops($new, $newmessageprops); - mapi_message_savechanges($new); - - // Submit message to non-resource recipients - mapi_message_submitmessage($new); - } - - // Set properties on meeting object in calendar - // Set requestsent to 'true' (turns on 'tracking', etc) - $props = array(); - $props[$this->proptags['meetingstatus']] = olMeeting; - $props[$this->proptags['responsestatus']] = olResponseOrganized; - $props[$this->proptags['requestsent']] = (!empty($recipients)) || ($this->includesResources && !$this->errorSetResource); - $props[$this->proptags['attendee_critical_change']] = time(); - $props[$this->proptags['owner_critical_change']] = time(); - $props[$this->proptags['meetingtype']] = mtgRequest; - // save the new updatecounter to exception/recurring series/normal meeting - $props[$this->proptags['updatecounter']] = $newmessageprops[$this->proptags['updatecounter']]; - - // PR_START_DATE and PR_END_DATE will be used by outlook to show the position in the calendar - $props[PR_START_DATE] = $messageprops[$this->proptags['startdate']]; - $props[PR_END_DATE] = $messageprops[$this->proptags['duedate']]; - - mapi_setprops($message, $props); - - // saving of these properties on calendar item should be handled by caller function - // based on sending meeting request was successfull or not - } - - /** - * OL2007 uses these 4 properties to specify occurence that should be updated. - * ical generates RECURRENCE-ID property based on exception's basedate (PidLidExceptionReplaceTime), - * but OL07 doesn't send this property, so ical will generate RECURRENCE-ID property based on date - * from GlobalObjId and time from StartRecurTime property, so we are sending basedate property and - * also additionally we are sending these properties. - * Ref: MS-OXCICAL 2.2.1.20.20 Property: RECURRENCE-ID - * @param Object $recurObject instance of recurrence class for this message - * @param Array $messageprops properties of meeting object that is going to be send - * @param Array $newmessageprops properties of meeting request/response that is going to be send - */ - function generateRecurDates($recurObject, $messageprops, &$newmessageprops) - { - if($messageprops[$this->proptags['startdate']] && $messageprops[$this->proptags['duedate']]) { - $startDate = date("Y:n:j:G:i:s", $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['startdate']])); - $endDate = date("Y:n:j:G:i:s", $recurObject->fromGMT($recurObject->tz, $messageprops[$this->proptags['duedate']])); - - $startDate = explode(":", $startDate); - $endDate = explode(":", $endDate); - - // [0] => year, [1] => month, [2] => day, [3] => hour, [4] => minutes, [5] => seconds - // RecurStartDate = year * 512 + month_number * 32 + day_number - $newmessageprops[$this->proptags["start_recur_date"]] = (((int) $startDate[0]) * 512) + (((int) $startDate[1]) * 32) + ((int) $startDate[2]); - // RecurStartTime = hour * 4096 + minutes * 64 + seconds - $newmessageprops[$this->proptags["start_recur_time"]] = (((int) $startDate[3]) * 4096) + (((int) $startDate[4]) * 64) + ((int) $startDate[5]); - - $newmessageprops[$this->proptags["end_recur_date"]] = (((int) $endDate[0]) * 512) + (((int) $endDate[1]) * 32) + ((int) $endDate[2]); - $newmessageprops[$this->proptags["end_recur_time"]] = (((int) $endDate[3]) * 4096) + (((int) $endDate[4]) * 64) + ((int) $endDate[5]); - } - } - - function createOutgoingMessage() - { - $sentprops = array(); - $outbox = $this->openDefaultOutbox($this->openDefaultStore()); - - $outgoing = mapi_folder_createmessage($outbox); - if(!$outgoing) return false; - - $addrinfo = $this->getOwnerAddress($this->store); - if($addrinfo) { - list($ownername, $owneremailaddr, $owneraddrtype, $ownerentryid, $ownersearchkey) = $addrinfo; - $sentprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $owneremailaddr; - $sentprops[PR_SENT_REPRESENTING_NAME] = $ownername; - $sentprops[PR_SENT_REPRESENTING_ADDRTYPE] = $owneraddrtype; - $sentprops[PR_SENT_REPRESENTING_ENTRYID] = $ownerentryid; - $sentprops[PR_SENT_REPRESENTING_SEARCH_KEY] = $ownersearchkey; - } - - $sentprops[PR_SENTMAIL_ENTRYID] = $this->getDefaultSentmailEntryID($this->openDefaultStore()); - - mapi_setprops($outgoing, $sentprops); - - return $outgoing; - } - - /** - * Function which checks received meeting request is either old(outofdate) or new. - * @return boolean true if meeting request is outofdate else false if it is new - */ - function isMeetingOutOfDate() - { - $result = false; - $store = $this->store; - $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['meetingtype'], $this->proptags['owner_critical_change'])); - - if (isset($props[$this->proptags['meetingtype']]) && ($props[$this->proptags['meetingtype']] & mtgOutOfDate) == mtgOutOfDate) { - return true; - } - - // get the basedate to check for exception - $basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]); - - $calendarItems = $this->getCorrespondedCalendarItems(); - - foreach($calendarItems as $calendarItem) { - if ($calendarItem) { - $calendarItemProps = mapi_getprops($calendarItem, array( - $this->proptags['owner_critical_change'], - $this->proptags['updatecounter'], - $this->proptags['recurring'] - )); - - // If these items is recurring and basedate is found then open exception to compare it with meeting request - if (isset($calendarItemProps[$this->proptags['recurring']]) && $calendarItemProps[$this->proptags['recurring']] && $basedate) { - $recurr = new Recurrence($store, $calendarItem); - - if ($recurr->isException($basedate)) { - $attach = $recurr->getExceptionAttachment($basedate); - $exception = mapi_attach_openobj($attach, 0); - $occurrenceItemProps = mapi_getprops($exception, array( - $this->proptags['owner_critical_change'], - $this->proptags['updatecounter'] - )); - } - - // we found the exception, compare with it - if(isset($occurrenceItemProps)) { - if ((isset($occurrenceItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $occurrenceItemProps[$this->proptags['updatecounter']]) - || (isset($occurrenceItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $occurrenceItemProps[$this->proptags['owner_critical_change']])) { - - mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033)); - mapi_savechanges($this->message); - $result = true; - } - } else { - // we are not able to find exception, could mean that a significant change has occured on series - // and it deleted all exceptions, so compare with series - if ((isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]) - || (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']])) { - - mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033)); - mapi_savechanges($this->message); - $result = true; - } - } - } else { - // normal / recurring series - if ((isset($calendarItemProps[$this->proptags['updatecounter']]) && $props[$this->proptags['updatecounter']] < $calendarItemProps[$this->proptags['updatecounter']]) - || (isset($calendarItemProps[$this->proptags['owner_critical_change']]) && $props[$this->proptags['owner_critical_change']] < $calendarItemProps[$this->proptags['owner_critical_change']])) { - - mapi_setprops($this->message, array($this->proptags['meetingtype'] => mtgOutOfDate, PR_ICON_INDEX => 1033)); - mapi_savechanges($this->message); - $result = true; - } - } - } - } - - return $result; - } - - /** - * Function which checks received meeting request is updated later or not. - * @return boolean true if meeting request is updated later. - * @TODO: Implement handling for recurrings and exceptions. - */ - function isMeetingUpdated() - { - $result = false; - $store = $this->store; - $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['updatecounter'], $this->proptags['owner_critical_change'], $this->proptags['updatecounter'])); - - $calendarItems = $this->getCorrespondedCalendarItems(); - - foreach($calendarItems as $calendarItem) { - if ($calendarItem) { - $calendarItemProps = mapi_getprops($calendarItem, array( - $this->proptags['updatecounter'], - $this->proptags['recurring'] - )); - - if(isset($calendarItemProps[$this->proptags['updatecounter']]) && isset($props[$this->proptags['updatecounter']]) && $calendarItemProps[$this->proptags['updatecounter']] > $props[$this->proptags['updatecounter']]) { - $result = true; - } - } - } - - return $result; - } - - /** - * Checks if there has been any significant changes on appointment/meeting item. - * Significant changes be: - * 1) startdate has been changed - * 2) duedate has been changed OR - * 3) recurrence pattern has been created, modified or removed - * - * @param Array oldProps old props before an update - * @param Number basedate basedate - * @param Boolean isRecurrenceChanged for change in recurrence pattern. - * isRecurrenceChanged true means Recurrence pattern has been changed, so clear all attendees response - */ - function checkSignificantChanges($oldProps, $basedate, $isRecurrenceChanged = false) - { - $message = null; - $attach = null; - - // If basedate is specified then we need to open exception message to clear recipient responses - if($basedate) { - $recurrence = new Recurrence($this->store, $this->message); - if($recurrence->isException($basedate)){ - $attach = $recurrence->getExceptionAttachment($basedate); - if ($attach) { - $message = mapi_attach_openobj($attach, MAPI_MODIFY); - } - } - } else { - // use normal message or recurring series message - $message = $this->message; - } - - if(!$message) { - return; - } - - $newProps = mapi_getprops($message, array($this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['updatecounter'])); - - // Check whether message is updated or not. - if(isset($newProps[$this->proptags['updatecounter']]) && $newProps[$this->proptags['updatecounter']] == 0) { - return; - } - - if (($newProps[$this->proptags['startdate']] != $oldProps[$this->proptags['startdate']]) - || ($newProps[$this->proptags['duedate']] != $oldProps[$this->proptags['duedate']]) - || $isRecurrenceChanged) { - $this->clearRecipientResponse($message); - - mapi_setprops($message, array($this->proptags['owner_critical_change'] => time())); - - mapi_savechanges($message); - if ($attach) { // Also save attachment Object. - mapi_savechanges($attach); - } - } - } - - /** - * Clear responses of all attendees who have replied in past. - * @param MAPI_MESSAGE $message on which responses should be cleared - */ - function clearRecipientResponse($message) - { - $recipTable = mapi_message_getrecipienttable($message); - $recipsRows = mapi_table_queryallrows($recipTable, $this->recipprops); - - foreach($recipsRows as $recipient) { - if(($recipient[PR_RECIPIENT_FLAGS] & recipOrganizer) != recipOrganizer){ - // Recipient is attendee, set the trackstatus to "Not Responded" - $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; - } else { - // Recipient is organizer, this is not possible, but for safety - // it is best to clear the trackstatus for him as well by setting - // the trackstatus to "Organized". - $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; - } - mapi_message_modifyrecipients($message, MODRECIP_MODIFY, array($recipient)); - } - } - - /** - * Function returns corresponded calendar items attached with - * the meeting request. - * @return Array array of correlated calendar items. - */ - function getCorrespondedCalendarItems() - { - $store = $this->store; - $props = mapi_getprops($this->message, array($this->proptags['goid'], $this->proptags['goid2'], PR_RCVD_REPRESENTING_NAME)); - - $basedate = $this->getBasedateFromGlobalID($props[$this->proptags['goid']]); - - // If Delegate is processing mr for Delegator then retrieve Delegator's store and calendar. - if (isset($props[PR_RCVD_REPRESENTING_NAME])) { - $delegatorStore = $this->getDelegatorStore($props); - $store = $delegatorStore['store']; - $calFolder = $delegatorStore['calFolder']; - } else { - $calFolder = $this->openDefaultCalendar(); - } - - // Finding item in calendar with GlobalID(0x3), not necessary that attendee is having recurring item, he/she can also have only a occurrence - $entryids = $this->findCalendarItems($props[$this->proptags['goid']], $calFolder); - - // Basedate found, so this meeting request is an update of an occurrence. - if ($basedate) { - if (!$entryids) { - // Find main recurring item in calendar with GlobalID(0x23) - $entryids = $this->findCalendarItems($props[$this->proptags['goid2']], $calFolder); - } - } - - $calendarItems = array(); - if ($entryids) { - foreach($entryids as $entryid) { - $calendarItems[] = mapi_msgstore_openentry($store, $entryid); - } - } - - return $calendarItems; - } - - /** - * Function which checks whether received meeting request is either conflicting with other appointments or not. - *@return mixed(boolean/integer) true if normal meeting is conflicting or an integer which specifies no of instances - * conflict of recurring meeting and false if meeting is not conflicting. - */ - function isMeetingConflicting($message = false, $userStore = false, $calFolder = false, $msgprops = false) - { - $returnValue = false; - $conflicting = false; - $noOfInstances = 0; - - if (!$message) $message = $this->message; - - if (!$userStore) $userStore = $this->store; - - if (!$calFolder) { - $root = mapi_msgstore_openentry($userStore); - $rootprops = mapi_getprops($root, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_FREEBUSY_ENTRYIDS)); - - if(!isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID])) { - return; - } - - $calFolder = mapi_msgstore_openentry($userStore, $rootprops[PR_IPM_APPOINTMENT_ENTRYID]); - } - - if (!$msgprops) $msgprops = mapi_getprops($message, array($this->proptags['goid'], $this->proptags['goid2'], $this->proptags['startdate'], $this->proptags['duedate'], $this->proptags['recurring'], $this->proptags['clipstart'], $this->proptags['clipend'])); - - if ($calFolder) { - // Meeting request is recurring, so get all occurrence and check for each occurrence whether it conflicts with other appointments in Calendar. - if (isset($msgprops[$this->proptags['recurring']]) && $msgprops[$this->proptags['recurring']]) { - // Apply recurrence class and retrieve all occurrences(max: 30 occurrence because recurrence can also be set as 'no end date') - $recurr = new Recurrence($userStore, $message); - $items = $recurr->getItems($msgprops[$this->proptags['clipstart']], $msgprops[$this->proptags['clipend']] * (24*24*60), 30); - - foreach ($items as $item) { - // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item - $calendarItems = $recurr->getCalendarItems($userStore, $calFolder, $item[$this->proptags['startdate']], $item[$this->proptags['duedate']], array($this->proptags['goid'], $this->proptags['busystatus'], PR_OWNER_APPT_ID)); - - foreach ($calendarItems as $calendarItem) { - if ($calendarItem[$this->proptags['busystatus']] != fbFree) { - /** - * Only meeting requests have globalID, normal appointments do not have globalID - * so if any normal appointment if found then it is assumed to be conflict. - */ - if(isset($calendarItem[$this->proptags['goid']])) { - if ($calendarItem[$this->proptags['goid']] !== $msgprops[$this->proptags['goid']]) { - $noOfInstances++; - break; - } - } else { - $noOfInstances++; - break; - } - } - } - } - - $returnValue = $noOfInstances; - } else { - // Get all items in the timeframe that we want to book, and get the goid and busystatus for each item - $items = getCalendarItems($userStore, $calFolder, $msgprops[$this->proptags['startdate']], $msgprops[$this->proptags['duedate']], array($this->proptags['goid'], $this->proptags['busystatus'], PR_OWNER_APPT_ID)); - - foreach($items as $item) { - if ($item[$this->proptags['busystatus']] != fbFree) { - if(isset($item[$this->proptags['goid']])) { - if (($item[$this->proptags['goid']] !== $msgprops[$this->proptags['goid']]) - && ($item[$this->proptags['goid']] !== $msgprops[$this->proptags['goid2']])) { - $conflicting = true; - break; - } - } else { - $conflicting = true; - break; - } - } - } - - if ($conflicting) $returnValue = true; - } - } - return $returnValue; - } - - /** - * Function which adds organizer to recipient list which is passed. - * This function also checks if it has organizer. - * - * @param array $messageProps message properties - * @param array $recipients recipients list of message. - * @param boolean $isException true if we are processing recipient of exception - */ - function addDelegator($messageProps, &$recipients) - { - $hasDelegator = false; - // Check if meeting already has an organizer. - foreach ($recipients as $key => $recipient){ - if (isset($messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) && $recipient[PR_EMAIL_ADDRESS] == $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]) - $hasDelegator = true; - } - - if (!$hasDelegator){ - // Create delegator. - $delegator = array(); - $delegator[PR_ENTRYID] = $messageProps[PR_RCVD_REPRESENTING_ENTRYID]; - $delegator[PR_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME]; - $delegator[PR_EMAIL_ADDRESS] = $messageProps[PR_RCVD_REPRESENTING_EMAIL_ADDRESS]; - $delegator[PR_RECIPIENT_TYPE] = MAPI_TO; - $delegator[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_RCVD_REPRESENTING_NAME]; - $delegator[PR_ADDRTYPE] = empty($messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]) ? 'SMTP':$messageProps[PR_RCVD_REPRESENTING_ADDRTYPE]; - $delegator[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; - $delegator[PR_RECIPIENT_FLAGS] = recipSendable; - - // Add organizer to recipients list. - array_unshift($recipients, $delegator); - } - } - - function getDelegatorStore($messageprops) - { - // Find the organiser of appointment in addressbook - $delegatorName = array(array(PR_DISPLAY_NAME => $messageprops[PR_RCVD_REPRESENTING_NAME])); - $ab = mapi_openaddressbook($this->session); - $user = mapi_ab_resolvename($ab, $delegatorName, EMS_AB_ADDRESS_LOOKUP); - - // Get StoreEntryID by username - $delegatorEntryid = mapi_msgstore_createentryid($this->store, $user[0][PR_EMAIL_ADDRESS]); - // Open store of the delegator - $delegatorStore = mapi_openmsgstore($this->session, $delegatorEntryid); - // Open root folder - $delegatorRoot = mapi_msgstore_openentry($delegatorStore, null); - // Get calendar entryID - $delegatorRootProps = mapi_getprops($delegatorRoot, array(PR_IPM_APPOINTMENT_ENTRYID)); - // Open the calendar Folder - $calFolder = mapi_msgstore_openentry($delegatorStore, $delegatorRootProps[PR_IPM_APPOINTMENT_ENTRYID]); - - return Array('store' => $delegatorStore, 'calFolder' => $calFolder); - } - - /** - * Function returns extra info about meeting timing along with message body - * which will be included in body while sending meeting request/response. - * - * @return string $meetingTimeInfo info about meeting timing along with message body - */ - function getMeetingTimeInfo() - { - return $this->meetingTimeInfo; - } - - /** - * Function sets extra info about meeting timing along with message body - * which will be included in body while sending meeting request/response. - * - * @param string $meetingTimeInfo info about meeting timing along with message body - */ - function setMeetingTimeInfo($meetingTimeInfo) - { - $this->meetingTimeInfo = $meetingTimeInfo; - } -} diff --git a/sources/backend/zarafa/mapi/class.recurrence.php b/sources/backend/zarafa/mapi/class.recurrence.php deleted file mode 100644 index fcb56e6..0000000 --- a/sources/backend/zarafa/mapi/class.recurrence.php +++ /dev/null @@ -1,1576 +0,0 @@ -. - * - */ - - include_once('backend/zarafa/mapi/class.baserecurrence.php'); - - /** - * Recurrence - * @author Steve Hardy - * @author Michel de Ron - */ - class Recurrence extends BaseRecurrence - { - /* - * ABOUT TIMEZONES - * - * Timezones are rather complicated here so here are some rules to think about: - * - * - Timestamps in mapi-like properties (so in PT_SYSTIME properties) are always in GMT (including - * the 'basedate' property in exceptions !!) - * - Timestamps for recurrence (so start/end of recurrence, and basedates for exceptions (everything - * outside the 'basedate' property in the exception !!), and start/endtimes for exceptions) are - * always in LOCAL time. - */ - - // All properties for a recipient that are interesting - var $recipprops = Array( - PR_ENTRYID, - PR_SEARCH_KEY, - PR_DISPLAY_NAME, - PR_EMAIL_ADDRESS, - PR_RECIPIENT_ENTRYID, - PR_RECIPIENT_TYPE, - PR_SEND_INTERNET_ENCODING, - PR_SEND_RICH_INFO, - PR_RECIPIENT_DISPLAY_NAME, - PR_ADDRTYPE, - PR_DISPLAY_TYPE, - PR_DISPLAY_TYPE_EX, - PR_RECIPIENT_TRACKSTATUS, - PR_RECIPIENT_TRACKSTATUS_TIME, - PR_RECIPIENT_FLAGS, - PR_ROWID - ); - - /** - * Constructor - * @param resource $store MAPI Message Store Object - * @param resource $message the MAPI (appointment) message - */ - function Recurrence($store, $message) - { - - $properties = array(); - $properties["entryid"] = PR_ENTRYID; - $properties["parent_entryid"] = PR_PARENT_ENTRYID; - $properties["message_class"] = PR_MESSAGE_CLASS; - $properties["icon_index"] = PR_ICON_INDEX; - $properties["subject"] = PR_SUBJECT; - $properties["display_to"] = PR_DISPLAY_TO; - $properties["importance"] = PR_IMPORTANCE; - $properties["sensitivity"] = PR_SENSITIVITY; - $properties["startdate"] = "PT_SYSTIME:PSETID_Appointment:0x820d"; - $properties["duedate"] = "PT_SYSTIME:PSETID_Appointment:0x820e"; - $properties["recurring"] = "PT_BOOLEAN:PSETID_Appointment:0x8223"; - $properties["recurring_data"] = "PT_BINARY:PSETID_Appointment:0x8216"; - $properties["busystatus"] = "PT_LONG:PSETID_Appointment:0x8205"; - $properties["label"] = "PT_LONG:PSETID_Appointment:0x8214"; - $properties["alldayevent"] = "PT_BOOLEAN:PSETID_Appointment:0x8215"; - $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506"; - $properties["meeting"] = "PT_LONG:PSETID_Appointment:0x8217"; - $properties["startdate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8235"; - $properties["enddate_recurring"] = "PT_SYSTIME:PSETID_Appointment:0x8236"; - $properties["recurring_pattern"] = "PT_STRING8:PSETID_Appointment:0x8232"; - $properties["location"] = "PT_STRING8:PSETID_Appointment:0x8208"; - $properties["duration"] = "PT_LONG:PSETID_Appointment:0x8213"; - $properties["responsestatus"] = "PT_LONG:PSETID_Appointment:0x8218"; - $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503"; - $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501"; - $properties["recurrencetype"] = "PT_LONG:PSETID_Appointment:0x8231"; - $properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a"; - $properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586"; - $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords"; - $properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502"; - $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516"; - $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517"; - $properties["basedate"] = "PT_SYSTIME:PSETID_Appointment:0x8228"; - $properties["timezone_data"] = "PT_BINARY:PSETID_Appointment:0x8233"; - $properties["timezone"] = "PT_STRING8:PSETID_Appointment:0x8234"; - $properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560"; - $properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510"; - $properties["hideattachments"] = "PT_BOOLEAN:PSETID_Common:0x8514"; - - $this->proptags = getPropIdsFromStrings($store, $properties); - - parent::BaseRecurrence($store, $message); - } - - /** - * Create an exception - * @param array $exception_props the exception properties (same properties as normal recurring items) - * @param date $base_date the base date of the exception (LOCAL time of non-exception occurrence) - * @param boolean $delete true - delete occurrence, false - create new exception or modify existing - * @param array $exception_recips true - delete occurrence, false - create new exception or modify existing - * @param mapi_message $copy_attach_from mapi message from which attachments should be copied - */ - function createException($exception_props, $base_date, $delete = false, $exception_recips = array(), $copy_attach_from = false) - { - $baseday = $this->dayStartOf($base_date); - $basetime = $baseday + $this->recur["startocc"] * 60; - - // Remove any pre-existing exception on this base date - if($this->isException($baseday)) { - $this->deleteException($baseday); // note that deleting an exception is different from creating a deleted exception (deleting an occurrence). - } - - if(!$delete) { - if(isset($exception_props[$this->proptags["startdate"]]) && !$this->isValidExceptionDate($base_date, $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]))) { - return false; - } - // Properties in the attachment are the properties of the base object, plus $exception_props plus the base date - foreach (array("subject", "location", "label", "reminder", "reminder_minutes", "alldayevent", "busystatus") as $propname) { - if(isset($this->messageprops[$this->proptags[$propname]])) - $props[$this->proptags[$propname]] = $this->messageprops[$this->proptags[$propname]]; - } - - $props[PR_MESSAGE_CLASS] = "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}"; - unset($exception_props[PR_MESSAGE_CLASS]); - unset($exception_props[PR_ICON_INDEX]); - $props = $exception_props + $props; - - // Basedate in the exception attachment is the GMT time at which the original occurrence would have been - $props[$this->proptags["basedate"]] = $this->toGMT($this->tz, $basetime); - - if (!isset($exception_props[$this->proptags["startdate"]])) { - $props[$this->proptags["startdate"]] = $this->getOccurrenceStart($base_date); - } - - if (!isset($exception_props[$this->proptags["duedate"]])) { - $props[$this->proptags["duedate"]] = $this->getOccurrenceEnd($base_date); - } - - // synchronize commonstart/commonend with startdate/duedate - if(isset($props[$this->proptags["startdate"]])) { - $props[$this->proptags["commonstart"]] = $props[$this->proptags["startdate"]]; - } - - if(isset($props[$this->proptags["duedate"]])) { - $props[$this->proptags["commonend"]] = $props[$this->proptags["duedate"]]; - } - - // Save the data into an attachment - $this->createExceptionAttachment($props, $exception_recips, $copy_attach_from); - - $changed_item = array(); - - $changed_item["basedate"] = $baseday; - $changed_item["start"] = $this->fromGMT($this->tz, $props[$this->proptags["startdate"]]); - $changed_item["end"] = $this->fromGMT($this->tz, $props[$this->proptags["duedate"]]); - - if(array_key_exists($this->proptags["subject"], $exception_props)) { - $changed_item["subject"] = $exception_props[$this->proptags["subject"]]; - } - - if(array_key_exists($this->proptags["location"], $exception_props)) { - $changed_item["location"] = $exception_props[$this->proptags["location"]]; - } - - if(array_key_exists($this->proptags["label"], $exception_props)) { - $changed_item["label"] = $exception_props[$this->proptags["label"]]; - } - - if(array_key_exists($this->proptags["reminder"], $exception_props)) { - $changed_item["reminder_set"] = $exception_props[$this->proptags["reminder"]]; - } - - if(array_key_exists($this->proptags["reminder_minutes"], $exception_props)) { - $changed_item["remind_before"] = $exception_props[$this->proptags["reminder_minutes"]]; - } - - if(array_key_exists($this->proptags["alldayevent"], $exception_props)) { - $changed_item["alldayevent"] = $exception_props[$this->proptags["alldayevent"]]; - } - - if(array_key_exists($this->proptags["busystatus"], $exception_props)) { - $changed_item["busystatus"] = $exception_props[$this->proptags["busystatus"]]; - } - - // Add the changed occurrence to the list - array_push($this->recur["changed_occurences"], $changed_item); - } else { - // Delete the occurrence by placing it in the deleted occurrences list - array_push($this->recur["deleted_occurences"], $baseday); - } - - // Turn on hideattachments, because the attachments in this item are the exceptions - mapi_setprops($this->message, array ( $this->proptags["hideattachments"] => true )); - - // Save recurrence data to message - $this->saveRecurrence(); - - return true; - } - - /** - * Modifies an existing exception, but only updates the given properties - * NOTE: You can't remove properites from an exception, only add new ones - */ - function modifyException($exception_props, $base_date, $exception_recips = array(), $copy_attach_from = false) - { - if(isset($exception_props[$this->proptags["startdate"]]) && !$this->isValidExceptionDate($base_date, $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]))) { - return false; - } - - $baseday = $this->dayStartOf($base_date); - $basetime = $baseday + $this->recur["startocc"] * 60; - $extomodify = false; - - for($i = 0, $len = count($this->recur["changed_occurences"]); $i < $len; $i++) { - if($this->isSameDay($this->recur["changed_occurences"][$i]["basedate"], $baseday)) - $extomodify = &$this->recur["changed_occurences"][$i]; - } - - if(!$extomodify) - return false; - - // remove basedate property as we want to preserve the old value - // client will send basedate with time part as zero, so discard that value - unset($exception_props[$this->proptags["basedate"]]); - - if(array_key_exists($this->proptags["startdate"], $exception_props)) { - $extomodify["start"] = $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]); - } - - if(array_key_exists($this->proptags["duedate"], $exception_props)) { - $extomodify["end"] = $this->fromGMT($this->tz, $exception_props[$this->proptags["duedate"]]); - } - - if(array_key_exists($this->proptags["subject"], $exception_props)) { - $extomodify["subject"] = $exception_props[$this->proptags["subject"]]; - } - - if(array_key_exists($this->proptags["location"], $exception_props)) { - $extomodify["location"] = $exception_props[$this->proptags["location"]]; - } - - if(array_key_exists($this->proptags["label"], $exception_props)) { - $extomodify["label"] = $exception_props[$this->proptags["label"]]; - } - - if(array_key_exists($this->proptags["reminder"], $exception_props)) { - $extomodify["reminder_set"] = $exception_props[$this->proptags["reminder"]]; - } - - if(array_key_exists($this->proptags["reminder_minutes"], $exception_props)) { - $extomodify["remind_before"] = $exception_props[$this->proptags["reminder_minutes"]]; - } - - if(array_key_exists($this->proptags["alldayevent"], $exception_props)) { - $extomodify["alldayevent"] = $exception_props[$this->proptags["alldayevent"]]; - } - - if(array_key_exists($this->proptags["busystatus"], $exception_props)) { - $extomodify["busystatus"] = $exception_props[$this->proptags["busystatus"]]; - } - - $exception_props[PR_MESSAGE_CLASS] = "IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}"; - - // synchronize commonstart/commonend with startdate/duedate - if(isset($exception_props[$this->proptags["startdate"]])) { - $exception_props[$this->proptags["commonstart"]] = $exception_props[$this->proptags["startdate"]]; - } - - if(isset($exception_props[$this->proptags["duedate"]])) { - $exception_props[$this->proptags["commonend"]] = $exception_props[$this->proptags["duedate"]]; - } - - $attach = $this->getExceptionAttachment($baseday); - if(!$attach) { - if ($copy_attach_from) { - $this->deleteExceptionAttachment($base_date); - $this->createException($exception_props, $base_date, false, $exception_recips, $copy_attach_from); - } else { - $this->createExceptionAttachment($exception_props, $exception_recips, $copy_attach_from); - } - } else { - $message = mapi_attach_openobj($attach, MAPI_MODIFY); - - // Set exception properties on embedded message and save - mapi_setprops($message, $exception_props); - $this->setExceptionRecipients($message, $exception_recips, false); - mapi_savechanges($message); - - // If a new start or duedate is provided, we update the properties 'PR_EXCEPTION_STARTTIME' and 'PR_EXCEPTION_ENDTIME' - // on the attachment which holds the embedded msg and save everything. - $props = array(); - if (isset($exception_props[$this->proptags["startdate"]])) { - $props[PR_EXCEPTION_STARTTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]); - } - if (isset($exception_props[$this->proptags["duedate"]])) { - $props[PR_EXCEPTION_ENDTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["duedate"]]); - } - if (!empty($props)) { - mapi_setprops($attach, $props); - } - - mapi_savechanges($attach); - } - - // Save recurrence data to message - $this->saveRecurrence(); - - return true; - } - - // Checks to see if the following is true: - // 1) The exception to be created doesn't create two exceptions starting on one day (however, they can END on the same day by modifying duration) - // 2) The exception to be created doesn't 'jump' over another occurrence (which may be an exception itself!) - // - // Both $basedate and $start are in LOCAL time - function isValidExceptionDate($basedate, $start) - { - // The way we do this is to look at the days that we're 'moving' the item in the exception. Each - // of these days may only contain the item that we're modifying. Any other item violates the rules. - - if($this->isException($basedate)) { - // If we're modifying an exception, we want to look at the days that we're 'moving' compared to where - // the exception used to be. - $oldexception = $this->getChangeException($basedate); - $prevday = $this->dayStartOf($oldexception["start"]); - } else { - // If its a new exception, we want to look at the original placement of this item. - $prevday = $basedate; - } - - $startday = $this->dayStartOf($start); - - // Get all the occurrences on the days between the basedate (may be reversed) - if($prevday < $startday) - $items = $this->getItems($this->toGMT($this->tz, $prevday), $this->toGMT($this->tz, $startday + 24 * 60 * 60)); - else - $items = $this->getItems($this->toGMT($this->tz, $startday), $this->toGMT($this->tz, $prevday + 24 * 60 * 60)); - - // There should now be exactly one item, namely the item that we are modifying. If there are any other items in the range, - // then we abort the change, since one of the rules has been violated. - return count($items) == 1; - } - - /** - * Check to see if the exception proposed at a certain basedate is allowed concerning reminder times: - * - * Both must be true: - * - reminder time of this item is not before the starttime of the previous recurring item - * - reminder time of the next item is not before the starttime of this item - * - * @param date $basedate the base date of the exception (LOCAL time of non-exception occurrence) - * @param string $reminderminutes reminder minutes which is set of the item - * @param date $startdate the startdate of the selected item - * @returns boolean if the reminder minutes value valid (FALSE if either of the rules above are FALSE) - */ - function isValidReminderTime($basedate, $reminderminutes, $startdate) - { - $isreminderrangeset = false; - - // get all occurence items before the seleceted items occurence starttime - $occitems = $this->getItems($this->messageprops[$this->proptags["startdate"]], $this->toGMT($this->tz, $basedate)); - - if(!empty($occitems)) { - // as occitems array is sorted in ascending order of startdate, to get the previous occurence we take the last items in occitems . - $previousitem_startdate = $occitems[count($occitems) - 1][$this->proptags["startdate"]]; - - // if our reminder is set before or equal to the beginning of the previous occurrence, then that's not allowed - if($startdate - ($reminderminutes*60) <= $previousitem_startdate) - return false; - } - - // Get the endtime of the current occurrence and find the next two occurrences (including the current occurrence) - $currentOcc = $this->getItems($this->toGMT($this->tz, $basedate), 0x7ff00000, 2, true); - - // If there are another two occurrences, then the first is the current occurrence, and the one after that - // is the next occurrence. - if(count($currentOcc) > 1) { - $next = $currentOcc[1]; - // Get reminder time of the next occurrence. - $nextOccReminderTime = $next[$this->proptags["startdate"]] - ($next[$this->proptags["reminder_minutes"]] * 60); - // If the reminder time of the next item is before the start of this item, then that's not allowed - if($nextOccReminderTime <= $startdate) - return false; - } - - // All was ok - return true; - } - - function setRecurrence($tz, $recur) - { - // only reset timezone if specified - if($tz) - $this->tz = $tz; - - $this->recur = $recur; - - if(!isset($this->recur["changed_occurences"])) - $this->recur["changed_occurences"] = Array(); - - if(!isset($this->recur["deleted_occurences"])) - $this->recur["deleted_occurences"] = Array(); - - $this->deleteAttachments(); - $this->saveRecurrence(); - - // if client has not set the recurring_pattern then we should generate it and save it - $messageProps = mapi_getprops($this->message, Array($this->proptags["recurring_pattern"])); - if(empty($messageProps[$this->proptags["recurring_pattern"]])) { - $this->saveRecurrencePattern(); - } - } - - // Returns the start or end time of the occurrence on the given base date. - // This assumes that the basedate you supply is in LOCAL time - function getOccurrenceStart($basedate) { - $daystart = $this->dayStartOf($basedate); - return $this->toGMT($this->tz, $daystart + $this->recur["startocc"] * 60); - } - - function getOccurrenceEnd($basedate) { - $daystart = $this->dayStartOf($basedate); - return $this->toGMT($this->tz, $daystart + $this->recur["endocc"] * 60); - } - - - // Backwards compatible code - function getOccurenceStart($basedate) { - return $this->getOccurrenceStart($basedate); - } - function getOccurenceEnd($basedate) { - return $this->getOccurrenceEnd($basedate); - } - - /** - * This function returns the next remindertime starting from $timestamp - * When no next reminder exists, false is returned. - * - * Note: Before saving this new reminder time (when snoozing), you must check for - * yourself if this reminder time is earlier than your snooze time, else - * use your snooze time and not this reminder time. - */ - function getNextReminderTime($timestamp) - { - /** - * Get next item from now until forever, but max 1 item with reminder set - * Note 0x7ff00000 instead of 0x7fffffff because of possible overflow failures when converting to GMT.... - * Here for getting next 10 occurences assuming that next here we will be able to find - * nextreminder occurence in 10 occureneces - */ - $items = $this->getItems($timestamp, 0x7ff00000, 10, true); - - // Initially setting nextreminder to false so when no next reminder exists, false is returned. - $nextreminder = false; - /** - * Loop through all reminder which we get in items variable - * and check whether the remindertime is greater than timestamp. - * On the first occurence of greater nextreminder break the loop - * and return the value to calling function. - */ - for($i = 0, $len = count($items); $i < $len; $i++) - { - $item = $items[$i]; - $tempnextreminder = $item[$this->proptags["startdate"]] - ( $item[$this->proptags["reminder_minutes"]] * 60 ); - - // If tempnextreminder is greater than timestamp then save it in nextreminder and break from the loop. - if($tempnextreminder > $timestamp) - { - $nextreminder = $tempnextreminder; - break; - } - } - return $nextreminder; - } - - /** - * Note: Static function, more like a utility function. - * - * Gets all the items (including recurring items) in the specified calendar in the given timeframe. Items are - * included as a whole if they overlap the interval <$start, $end> (non-inclusive). This means that if the interval - * is <08:00 - 14:00>, the item [6:00 - 8:00> is NOT included, nor is the item [14:00 - 16:00>. However, the item - * [7:00 - 9:00> is included as a whole, and is NOT capped to [8:00 - 9:00>. - * - * @param $store resource The store in which the calendar resides - * @param $calendar resource The calendar to get the items from - * @param $viewstart int Timestamp of beginning of view window - * @param $viewend int Timestamp of end of view window - * @param $propsrequested array Array of properties to return - * @param $rows array Array of rowdata as if they were returned directly from mapi_table_queryrows. Each recurring item is - * expanded so that it seems that there are only many single appointments in the table. - */ - static function getCalendarItems($store, $calendar, $viewstart, $viewend, $propsrequested) - { - return getCalendarItems($store, $calendar, $viewstart, $viewend, $propsrequested); - } - - - /***************************************************************************************************************** - * CODE BELOW THIS LINE IS FOR INTERNAL USE ONLY - ***************************************************************************************************************** - */ - - /** - * Generates and stores recurrence pattern string to recurring_pattern property. - */ - function saveRecurrencePattern() - { - // Start formatting the properties in such a way we can apply - // them directly into the recurrence pattern. - $type = $this->recur['type']; - $everyn = $this->recur['everyn']; - $start = $this->recur['start']; - $end = $this->recur['end']; - $term = $this->recur['term']; - $numocc = isset($this->recur['numoccur']) ? $this->recur['numoccur'] : false; - $startocc = $this->recur['startocc']; - $endocc = $this->recur['endocc']; - $pattern = ''; - $occSingleDayRank = false; - $occTimeRange = ($startocc != 0 && $endocc != 0); - - switch ($type) { - // Daily - case 0x0A: - if ($everyn == 1) { - $type = _('workday'); - $occSingleDayRank = true; - } else if ($everyn == (24 * 60)) { - $type = _('day'); - $occSingleDayRank = true; - } else { - $everyn /= (24 * 60); - $type = _('days'); - $occSingleDayRank = false; - } - break; - // Weekly - case 0x0B: - if ($everyn == 1) { - $type = _('week'); - $occSingleDayRank = true; - } else { - $type = _('weeks'); - $occSingleDayRank = false; - } - break; - // Monthly - case 0x0C: - if ($everyn == 1) { - $type = _('month'); - $occSingleDayRank = true; - } else { - $type = _('months'); - $occSingleDayRank = false; - } - break; - // Yearly - case 0x0D: - if ($everyn <= 12) { - $everyn = 1; - $type = _('year'); - $occSingleDayRank = true; - } else { - $everyn = $everyn / 12; - $type = _('years'); - $occSingleDayRank = false; - } - break; - } - - // get timings of the first occurence - $firstoccstartdate = isset($startocc) ? $start + (((int) $startocc) * 60) : $start; - $firstoccenddate = isset($endocc) ? $end + (((int) $endocc) * 60) : $end; - - $start = gmdate(_('d-m-Y'), $firstoccstartdate); - $end = gmdate(_('d-m-Y'), $firstoccenddate); - $startocc = gmdate(_('G:i'), $firstoccstartdate); - $endocc = gmdate(_('G:i'), $firstoccenddate); - - // Based on the properties, we need to generate the recurrence pattern string. - // This is obviously very easy since we can simply concatenate a bunch of strings, - // however this messes up translations for languages which order their words - // differently. - // To improve translation quality we create a series of default strings, in which - // we only have to fill in the correct variables. The base string is thus selected - // based on the available properties. - if ($term == 0x23) { - // Never ends - if ($occTimeRange) { - if ($occSingleDayRank) { - $pattern = sprintf(_('Occurs every %s effective %s from %s to %s.'), $type, $start, $startocc, $endocc); - } else { - $pattern = sprintf(_('Occurs every %s %s effective %s from %s to %s.'), $everyn, $type, $start, $startocc, $endocc); - } - } else { - if ($occSingleDayRank) { - $pattern = sprintf(_('Occurs every %s effective %s.'), $type, $start); - } else { - $pattern = sprintf(_('Occurs every %s %s effective %s.'), $everyn, $type, $start); - } - } - } else if ($term == 0x22) { - // After a number of times - if ($occTimeRange) { - if ($occSingleDayRank) { - $pattern = sprintf(ngettext('Occurs every %s effective %s for %s occurence from %s to %s.', - 'Occurs every %s effective %s for %s occurences from %s to %s.', $numocc), $type, $start, $numocc, $startocc, $endocc); - } else { - $pattern = sprintf(ngettext('Occurs every %s %s effective %s for %s occurence from %s to %s.', - 'Occurs every %s %s effective %s for %s occurences %s to %s.', $numocc), $everyn, $type, $start, $numocc, $startocc, $endocc); - } - } else { - if ($occSingleDayRank) { - $pattern = sprintf(ngettext('Occurs every %s effective %s for %s occurence.', - 'Occurs every %s effective %s for %s occurences.', $numocc), $type, $start, $numocc); - } else { - $pattern = sprintf(ngettext('Occurs every %s %s effective %s for %s occurence.', - 'Occurs every %s %s effective %s for %s occurences.', $numocc), $everyn, $type, $start, $numocc); - } - } - } else if ($term == 0x21) { - // After the given enddate - if ($occTimeRange) { - if ($occSingleDayRank) { - $pattern = sprintf(_('Occurs every %s effective %s until %s from %s to %s.'), $type, $start, $end, $startocc, $endocc); - } else { - $pattern = sprintf(_('Occurs every %s %s effective %s until %s from %s to %s.'), $everyn, $type, $start, $end, $startocc, $endocc); - } - } else { - if ($occSingleDayRank) { - $pattern = sprintf(_('Occurs every %s effective %s until %s.'), $type, $start, $end); - } else { - $pattern = sprintf(_('Occurs every %s %s effective %s until %s.'), $everyn, $type, $start, $end); - } - } - } - - if(!empty($pattern)) { - mapi_setprops($this->message, Array($this->proptags["recurring_pattern"] => $pattern )); - } - } - - /* - * Remove an exception by base_date. This is the base date in local daystart time - */ - function deleteException($base_date) - { - // Remove all exceptions on $base_date from the deleted and changed occurrences lists - - // Remove all items in $todelete from deleted_occurences - $new = Array(); - - foreach($this->recur["deleted_occurences"] as $entry) { - if($entry != $base_date) - $new[] = $entry; - } - $this->recur["deleted_occurences"] = $new; - - $new = Array(); - - foreach($this->recur["changed_occurences"] as $entry) { - if(!$this->isSameDay($entry["basedate"], $base_date)) - $new[] = $entry; - else - $this->deleteExceptionAttachment($this->toGMT($this->tz, $base_date + $this->recur["startocc"] * 60)); - } - - $this->recur["changed_occurences"] = $new; - } - - /** - * Function which saves the exception data in an attachment. - * @param array $exception_props the exception data (like any other MAPI appointment) - * @param array $exception_recips list of recipients - * @param mapi_message $copy_attach_from mapi message from which attachments should be copied - * @return array properties of the exception - */ - function createExceptionAttachment($exception_props, $exception_recips = array(), $copy_attach_from = false) - { - // Create new attachment. - $attachment = mapi_message_createattach($this->message); - $props = array(); - $props[PR_ATTACHMENT_FLAGS] = 2; - $props[PR_ATTACHMENT_HIDDEN] = true; - $props[PR_ATTACHMENT_LINKID] = 0; - $props[PR_ATTACH_FLAGS] = 0; - $props[PR_ATTACH_METHOD] = 5; - $props[PR_DISPLAY_NAME] = "Exception"; - $props[PR_EXCEPTION_STARTTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["startdate"]]); - $props[PR_EXCEPTION_ENDTIME] = $this->fromGMT($this->tz, $exception_props[$this->proptags["duedate"]]); - mapi_message_setprops($attachment, $props); - - $imessage = mapi_attach_openobj($attachment, MAPI_CREATE | MAPI_MODIFY); - - if ($copy_attach_from) { - $attachmentTable = mapi_message_getattachmenttable($copy_attach_from); - if($attachmentTable) { - $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); - - foreach($attachments as $attach_props){ - $attach_old = mapi_message_openattach($copy_attach_from, (int) $attach_props[PR_ATTACH_NUM]); - $attach_newResourceMsg = mapi_message_createattach($imessage); - mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0); - mapi_savechanges($attach_newResourceMsg); - } - } - } - - $props = $props + $exception_props; - - // FIXME: the following piece of code is written to fix the creation - // of an exception. This is only a quickfix as it is not yet possible - // to change an existing exception. - // remove mv properties when needed - foreach($props as $propTag=>$propVal){ - if ((mapi_prop_type($propTag) & MV_FLAG) == MV_FLAG && is_null($propVal)){ - unset($props[$propTag]); - } - } - - mapi_message_setprops($imessage, $props); - - $this->setExceptionRecipients($imessage, $exception_recips, true); - - mapi_message_savechanges($imessage); - mapi_message_savechanges($attachment); - } - - /** - * Function which deletes the attachment of an exception. - * @param date $base_date base date of the attachment. Should be in GMT. The attachment - * actually saves the real time of the original date, so we have - * to check whether it's on the same day. - */ - function deleteExceptionAttachment($base_date) - { - $attachments = mapi_message_getattachmenttable($this->message); - $attachTable = mapi_table_queryallrows($attachments, Array(PR_ATTACH_NUM)); - - foreach($attachTable as $attachRow) - { - $tempattach = mapi_message_openattach($this->message, $attachRow[PR_ATTACH_NUM]); - $exception = mapi_attach_openobj($tempattach); - - $data = mapi_message_getprops($exception, array($this->proptags["basedate"])); - - if($this->dayStartOf($this->fromGMT($this->tz, $data[$this->proptags["basedate"]])) == $this->dayStartOf($base_date)) { - mapi_message_deleteattach($this->message, $attachRow[PR_ATTACH_NUM]); - } - } - } - - /** - * Function which deletes all attachments of a message. - */ - function deleteAttachments() - { - $attachments = mapi_message_getattachmenttable($this->message); - $attachTable = mapi_table_queryallrows($attachments, Array(PR_ATTACH_NUM, PR_ATTACHMENT_HIDDEN)); - - foreach($attachTable as $attachRow) - { - if(isset($attachRow[PR_ATTACHMENT_HIDDEN]) && $attachRow[PR_ATTACHMENT_HIDDEN]) { - mapi_message_deleteattach($this->message, $attachRow[PR_ATTACH_NUM]); - } - } - } - - /** - * Get an exception attachment based on its basedate - */ - function getExceptionAttachment($base_date) - { - // Retrieve only embedded messages - $attach_res = Array(RES_AND, - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => PR_ATTACH_METHOD, - VALUE => array(PR_ATTACH_METHOD => 5) - ) - ) - ) - ); - $attachments = mapi_message_getattachmenttable($this->message); - $attachRows = mapi_table_queryallrows($attachments, Array(PR_ATTACH_NUM), $attach_res); - - if(is_array($attachRows)) { - foreach($attachRows as $attachRow) - { - $tempattach = mapi_message_openattach($this->message, $attachRow[PR_ATTACH_NUM]); - $exception = mapi_attach_openobj($tempattach); - - $data = mapi_message_getprops($exception, array($this->proptags["basedate"])); - - if(isset($data[$this->proptags["basedate"]]) && $this->isSameDay($this->fromGMT($this->tz,$data[$this->proptags["basedate"]]), $base_date)) { - return $tempattach; - } - } - } - - return false; - } - - /** - * processOccurrenceItem, adds an item to a list of occurrences, but only if the following criteria are met: - * - The resulting occurrence (or exception) starts or ends in the interval <$start, $end> - * - The ocurrence isn't specified as a deleted occurrence - * @param array $items reference to the array to be added to - * @param date $start start of timeframe in GMT TIME - * @param date $end end of timeframe in GMT TIME - * @param date $basedate (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE - * @param int $startocc start of occurrence since beginning of day in minutes - * @param int $endocc end of occurrence since beginning of day in minutes - * @param int $tz the timezone info for this occurrence ( applied to $basedate / $startocc / $endocc ) - * @param bool $reminderonly If TRUE, only add the item if the reminder is set - */ - function processOccurrenceItem(&$items, $start, $end, $basedate, $startocc, $endocc, $tz, $reminderonly) - { - $exception = $this->isException($basedate); - if($exception){ - return false; - }else{ - $occstart = $basedate + $startocc * 60; - $occend = $basedate + $endocc * 60; - - // Convert to GMT - $occstart = $this->toGMT($tz, $occstart); - $occend = $this->toGMT($tz, $occend); - - /** - * FIRST PART : Check range criterium. Exact matches (eg when $occstart == $end), do NOT match since you cannot - * see any part of the appointment. Partial overlaps DO match. - * - * SECOND PART : check if occurence is not a zero duration occurrence which - * starts at 00:00 and ends on 00:00. if it is so, then process - * the occurrence and send it in response. - */ - if(($occstart >= $end || $occend <= $start) && !($occstart == $occend && $occstart == $start)) - return; - - // Properties for this occurrence are the same as the main object, - // With these properties overridden - $newitem = $this->messageprops; - $newitem[$this->proptags["startdate"]] = $occstart; - $newitem[$this->proptags["duedate"]] = $occend; - $newitem[$this->proptags["commonstart"]] = $occstart; - $newitem[$this->proptags["commonend"]] = $occend; - $newitem["basedate"] = $basedate; - } - - // If reminderonly is set, only add reminders - if($reminderonly && (!isset($newitem[$this->proptags["reminder"]]) || $newitem[$this->proptags["reminder"]] == false)) - return; - - $items[] = $newitem; - } - - /** - * processExceptionItem, adds an all exception item to a list of occurrences, without any constraint on timeframe - * @param array $items reference to the array to be added to - * @param date $start start of timeframe in GMT TIME - * @param date $end end of timeframe in GMT TIME - */ - function processExceptionItems(&$items, $start, $end) - { - $limit = 0; - foreach($this->recur["changed_occurences"] as $exception) { - - // Convert to GMT - $occstart = $this->toGMT($this->tz, $exception["start"]); - $occend = $this->toGMT($this->tz, $exception["end"]); - - // Check range criterium. Exact matches (eg when $occstart == $end), do NOT match since you cannot - // see any part of the appointment. Partial overlaps DO match. - if($occstart >= $end || $occend <= $start) - continue; - - array_push($items, $this->getExceptionProperties($exception)); - if($limit && (count($items) == $limit)) - break; - } - } - - /** - * Function which verifies if on the given date an exception, delete or change, occurs. - * @param date $date the date - * @return array the exception, true - if an occurrence is deleted on the given date, false - no exception occurs on the given date - */ - function isException($basedate) - { - if($this->isDeleteException($basedate)) - return true; - - if($this->getChangeException($basedate) != false) - return true; - - return false; - } - - /** - * Returns TRUE if there is a DELETE exception on the given base date - */ - function isDeleteException($basedate) - { - // Check if the occurrence is deleted on the specified date - foreach($this->recur["deleted_occurences"] as $deleted) - { - if($this->isSameDay($deleted, $basedate)) - return true; - } - - return false; - } - - /** - * Returns the exception if there is a CHANGE exception on the given base date, or FALSE otherwise - */ - function getChangeException($basedate) - { - // Check if the occurrence is modified on the specified date - foreach($this->recur["changed_occurences"] as $changed) - { - if($this->isSameDay($changed["basedate"], $basedate)) - return $changed; - } - - return false; - } - - /** - * Function to see if two dates are on the same day - * @param date $time1 date 1 - * @param date $time2 date 2 - * @return boolean Returns TRUE when both dates are on the same day - */ - function isSameDay($date1, $date2) - { - $time1 = $this->gmtime($date1); - $time2 = $this->gmtime($date2); - - return $time1["tm_mon"] == $time2["tm_mon"] && $time1["tm_year"] == $time2["tm_year"] && $time1["tm_mday"] == $time2["tm_mday"]; - } - - /** - * Function to get all properties of a single changed exception. - * @param date $date base date of exception - * @return array associative array of properties for the exception, compatible with - */ - function getExceptionProperties($exception) - { - // Exception has same properties as main object, with some properties overridden: - $item = $this->messageprops; - - // Special properties - $item["exception"] = true; - $item["basedate"] = $exception["basedate"]; // note that the basedate is always in local time ! - - // MAPI-compatible properties (you can handle an exception as a normal calendar item like this) - $item[$this->proptags["startdate"]] = $this->toGMT($this->tz, $exception["start"]); - $item[$this->proptags["duedate"]] = $this->toGMT($this->tz, $exception["end"]); - $item[$this->proptags["commonstart"]] = $item[$this->proptags["startdate"]]; - $item[$this->proptags["commonend"]] = $item[$this->proptags["duedate"]]; - - if(isset($exception["subject"])) { - $item[$this->proptags["subject"]] = $exception["subject"]; - } - - if(isset($exception["label"])) { - $item[$this->proptags["label"]] = $exception["label"]; - } - - if(isset($exception["alldayevent"])) { - $item[$this->proptags["alldayevent"]] = $exception["alldayevent"]; - } - - if(isset($exception["location"])) { - $item[$this->proptags["location"]] = $exception["location"]; - } - - if(isset($exception["remind_before"])) { - $item[$this->proptags["reminder_minutes"]] = $exception["remind_before"]; - } - - if(isset($exception["reminder_set"])) { - $item[$this->proptags["reminder"]] = $exception["reminder_set"]; - } - - if(isset($exception["busystatus"])) { - $item[$this->proptags["busystatus"]] = $exception["busystatus"]; - } - - return $item; - } - - /** - * Function which sets recipients for an exception. - * - * The $exception_recips can be provided in 2 ways: - * - A delta which indicates which recipients must be added, removed or deleted. - * - A complete array of the recipients which should be applied to the message. - * - * The first option is preferred as it will require less work to be executed. - * - * @param resource $message exception attachment of recurring item - * @param array $exception_recips list of recipients - * @param boolean $copy_orig_recips True to copy all recipients which are on the original - * message to the attachment by default. False if only the $exception_recips changes should - * be applied. - */ - function setExceptionRecipients($message, $exception_recips, $copy_orig_recips = true) - { - if (isset($exception_recips['add']) || isset($exception_recips['remove']) || isset($exception_recips['modify'])) { - $this->setDeltaExceptionRecipients($message, $exception_recips, $copy_orig_recips); - } else { - $this->setAllExceptionRecipients($message, $exception_recips); - } - } - - /** - * Function which applies the provided delta for recipients changes to the exception. - * - * The $exception_recips should be an array containing the following keys: - * - "add": this contains an array of recipients which must be added - * - "remove": This contains an array of recipients which must be removed - * - "modify": This contains an array of recipients which must be modified - * - * @param resource $message exception attachment of recurring item - * @param array $exception_recips list of recipients - * @param boolean $copy_orig_recips True to copy all recipients which are on the original - * message to the attachment by default. False if only the $exception_recips changes should - * be applied. - */ - function setDeltaExceptionRecipients($exception, $exception_recips, $copy_orig_recips) - { - // Check if the recipients from the original message should be copied, - // if so, open the recipient table of the parent message and apply all - // rows on the target recipient. - if ($copy_orig_recips === true) { - $origTable = mapi_message_getrecipienttable($this->message); - $recipientRows = mapi_table_queryallrows($origTable, $this->recipprops); - mapi_message_modifyrecipients($exception, MODRECIP_ADD, $recipientRows); - } - - // Add organizer to meeting only if it is not organized. - $msgprops = mapi_getprops($exception, array(PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_ADDRTYPE, $this->proptags['responsestatus'])); - if (isset($msgprops[$this->proptags['responsestatus']]) && $msgprops[$this->proptags['responsestatus']] != olResponseOrganized){ - $this->addOrganizer($msgprops, $exception_recips['add']); - } - - // Remove all deleted recipients - if (isset($exception_recips['remove'])) { - foreach ($exception_recips['remove'] as &$recip) { - if (!isset($recipient[PR_RECIPIENT_FLAGS]) || $recip[PR_RECIPIENT_FLAGS] != (recipReserved | recipExceptionalDeleted | recipSendable)) { - $recip[PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalDeleted; - } else { - $recip[PR_RECIPIENT_FLAGS] = recipReserved | recipExceptionalDeleted | recipSendable; - } - $recip[PR_RECIPIENT_TRACKSTATUS] = olResponseNone; // No Response required - } - unset($recip); - mapi_message_modifyrecipients($exception, MODRECIP_MODIFY, $exception_recips['remove']); - } - - // Add all new recipients - if (isset($exception_recips['add'])) { - mapi_message_modifyrecipients($exception, MODRECIP_ADD, $exception_recips['add']); - } - - // Modify the existing recipients - if (isset($exception_recips['modify'])) { - mapi_message_modifyrecipients($exception, MODRECIP_MODIFY, $exception_recips['modify']); - } - } - - /** - * Function which applies the provided recipients to the exception, also checks for deleted recipients. - * - * The $exception_recips should be an array containing all recipients which must be applied - * to the exception. This will copy all recipients from the original message and then start filter - * out all recipients which are not provided by the $exception_recips list. - * - * @param resource $message exception attachment of recurring item - * @param array $exception_recips list of recipients - */ - function setAllExceptionRecipients($message, $exception_recips) - { - $deletedRecipients = array(); - $useMessageRecipients = false; - - $recipientTable = mapi_message_getrecipienttable($message); - $recipientRows = mapi_table_queryallrows($recipientTable, $this->recipprops); - - if (empty($recipientRows)) { - $useMessageRecipients = true; - $recipientTable = mapi_message_getrecipienttable($this->message); - $recipientRows = mapi_table_queryallrows($recipientTable, $this->recipprops); - } - - // Add organizer to meeting only if it is not organized. - $msgprops = mapi_getprops($message, array(PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_ADDRTYPE, $this->proptags['responsestatus'])); - if (isset($msgprops[$this->proptags['responsestatus']]) && $msgprops[$this->proptags['responsestatus']] != olResponseOrganized){ - $this->addOrganizer($msgprops, $exception_recips); - } - - if (!empty($exception_recips)) { - foreach($recipientRows as $key => $recipient) { - $found = false; - foreach($exception_recips as $excep_recip) { - if (isset($recipient[PR_SEARCH_KEY]) && isset($excep_recip[PR_SEARCH_KEY]) && $recipient[PR_SEARCH_KEY] == $excep_recip[PR_SEARCH_KEY]) - $found = true; - } - - if (!$found) { - $foundInDeletedRecipients = false; - // Look if the $recipient is in the list of deleted recipients - if (!empty($deletedRecipients)) { - foreach($deletedRecipients as $recip) { - if ($recip[PR_SEARCH_KEY] == $recipient[PR_SEARCH_KEY]){ - $foundInDeletedRecipients = true; - break; - } - } - } - - // If recipient is not in list of deleted recipient, add him - if (!$foundInDeletedRecipients) { - if (!isset($recipient[PR_RECIPIENT_FLAGS]) || $recipient[PR_RECIPIENT_FLAGS] != (recipReserved | recipExceptionalDeleted | recipSendable)) { - $recipient[PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalDeleted; - } else { - $recipient[PR_RECIPIENT_FLAGS] = recipReserved | recipExceptionalDeleted | recipSendable; - } - $recipient[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; // No Response required - $deletedRecipients[] = $recipient; - } - } - - // When $message contains a non-empty recipienttable, we must delete the recipients - // before re-adding them. However, when $message is doesn't contain any recipients, - // we are using the recipient table of the original message ($this->message) - // rather then $message. In that case, we don't need to remove the recipients - // from the $message, as the recipient table is already empty, and - // mapi_message_modifyrecipients() will throw an error. - if ($useMessageRecipients === false) { - mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient)); - } - } - $exception_recips = array_merge($exception_recips, $deletedRecipients); - } else { - $exception_recips = $recipientRows; - } - - if (!empty($exception_recips)) { - // Set the new list of recipients on the exception message, this also removes the existing recipients - mapi_message_modifyrecipients($message, 0, $exception_recips); - } - } - - /** - * Function returns basedates of all changed occurrences - *@return array array( - 0 => 123459321 - ) - */ - function getAllExceptions() - { - $result = false; - if (!empty($this->recur["changed_occurences"])) { - $result = array(); - foreach($this->recur["changed_occurences"] as $exception) { - $result[] = $exception["basedate"]; - } - return $result; - } - return $result; - } - - /** - * Function which adds organizer to recipient list which is passed. - * This function also checks if it has organizer. - * - * @param array $messageProps message properties - * @param array $recipients recipients list of message. - * @param boolean $isException true if we are processing recipient of exception - */ - function addOrganizer($messageProps, &$recipients, $isException = false){ - - $hasOrganizer = false; - // Check if meeting already has an organizer. - foreach ($recipients as $key => $recipient){ - if (isset($recipient[PR_RECIPIENT_FLAGS]) && $recipient[PR_RECIPIENT_FLAGS] == (recipSendable | recipOrganizer)) { - $hasOrganizer = true; - } else if ($isException && !isset($recipient[PR_RECIPIENT_FLAGS])){ - // Recipients for an occurrence - $recipients[$key][PR_RECIPIENT_FLAGS] = recipSendable | recipExceptionalResponse; - } - } - - if (!$hasOrganizer){ - // Create organizer. - $organizer = array(); - $organizer[PR_ENTRYID] = $messageProps[PR_SENT_REPRESENTING_ENTRYID]; - $organizer[PR_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME]; - $organizer[PR_EMAIL_ADDRESS] = $messageProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS]; - $organizer[PR_RECIPIENT_TYPE] = MAPI_TO; - $organizer[PR_RECIPIENT_DISPLAY_NAME] = $messageProps[PR_SENT_REPRESENTING_NAME]; - $organizer[PR_ADDRTYPE] = empty($messageProps[PR_SENT_REPRESENTING_ADDRTYPE])?'SMTP':$messageProps[PR_SENT_REPRESENTING_ADDRTYPE]; - $organizer[PR_RECIPIENT_TRACKSTATUS] = olRecipientTrackStatusNone; - $organizer[PR_RECIPIENT_FLAGS] = recipSendable | recipOrganizer; - - // Add organizer to recipients list. - array_unshift($recipients, $organizer); - } - } - } - - /* - - From http://www.ohelp-one.com/new-6765483-3268.html: - - Recurrence Data Structure Offset Type Value - - 0 ULONG (?) Constant : { 0x04, 0x30, 0x04, 0x30} - - 4 UCHAR 0x0A + recurrence type: 0x0A for daily, 0x0B for weekly, 0x0C for - monthly, 0x0D for yearly - - 5 UCHAR Constant: { 0x20} - - 6 ULONG Seems to be a variant of the recurrence type: 1 for daily every n - days, 2 for daily every weekday and weekly, 3 for monthly or yearly. The - special exception is regenerating tasks that regenerate on a weekly basis: 0 - is used in that case (I have no idea why). - - Here's the recurrence-type-specific data. Because the daily every N days - data are 4 bytes shorter than the data for the other types, the offsets for - the rest of the data will be 4 bytes off depending on the recurrence type. - - Daily every N days: - - 10 ULONG ( N - 1) * ( 24 * 60). I'm not sure what this is used for, but it's consistent. - - 14 ULONG N * 24 * 60: minutes between recurrences - - 18 ULONG 0 for all events and non-regenerating recurring tasks. 1 for - regenerating tasks. - - Daily every weekday (this is essentially a subtype of weekly recurrence): - - 10 ULONG 6 * 24 * 60: minutes between recurrences ( a week... sort of) - - 14 ULONG 1: recur every week (corresponds to the second parameter for weekly - recurrence) - - 18 ULONG 0 for all events and non-regenerating recurring tasks. 1 for - regenerating tasks. - - 22 ULONG 0x3E: bitmask for recurring every weekday (corresponds to fourth - parameter for weekly recurrence) - - Weekly every N weeks for all events and non-regenerating tasks: - - 10 ULONG 6 * 24 * 60: minutes between recurrences (a week... sort of) - - 14 ULONG N: recurrence interval - - 18 ULONG Constant: 0 - - 22 ULONG Bitmask for determining which days of the week the event recurs on - ( 1 << dayOfWeek, where Sunday is 0). - - Weekly every N weeks for regenerating tasks: 10 ULONG Constant: 0 - - 14 ULONG N * 7 * 24 * 60: recurrence interval in minutes between occurrences - - 18 ULONG Constant: 1 - - Monthly every N months on day D: - - 10 ULONG This is the most complicated value - in the entire mess. It's basically a very complicated way of stating the - recurrence interval. I tweaked fbs' basic algorithm. DateTime::MonthInDays - simply returns the number of days in a given month, e.g. 31 for July for 28 - for February (the algorithm doesn't take into account leap years, but it - doesn't seem to matter). My DateTime object, like Microsoft's COleDateTime, - uses 1-based months (i.e. January is 1, not 0). With that in mind, this - works: - - long monthIndex = ( ( ( ( 12 % schedule-=GetInterval()) * - - ( ( schedule-=GetStartDate().GetYear() - 1601) % - - schedule-=GetInterval())) % schedule-=GetInterval()) + - - ( schedule-=GetStartDate().GetMonth() - 1)) % schedule-=GetInterval(); - - for( int i = 0; i < monthIndex; i++) - - { - - value += DateTime::GetDaysInMonth( ( i % 12) + 1) * 24 * 60; - - } - - This should work for any recurrence interval, including those greater than - 12. - - 14 ULONG N: recurrence interval - - 18 ULONG 0 for all events and non-regenerating recurring tasks. 1 for - regenerating tasks. - - 22 ULONG D: day of month the event recurs on (if this value is greater than - the number of days in a given month [e.g. 31 for and recurs in June], then - the event will recur on the last day of the month) - - Monthly every N months on the Xth Y (e.g. "2nd Tuesday"): - - 10 ULONG See above: same as for monthly every N months on day D - - 14 ULONG N: recurrence interval - - 18 ULONG 0 for all events and non-regenerating recurring tasks. 1 for - regenerating tasks. - - 22 ULONG Y: bitmask for determining which day of the week the event recurs - on (see weekly every N weeks). Some useful values are 0x7F for any day, 0x3E - for a weekday, or 0x41 for a weekend day. - - 26 ULONG X: 1 for first occurrence, 2 for second, etc. 5 for last - occurrence. E.g. for "2nd Tuesday", you should have values of 0x04 for the - prior value and 2 for this one. - - Yearly on day D of month M: - - 10 ULONG M (sort of): This is another messy - value. It's the number of minute since the startning of the year to the - given month. For an explanation of GetDaysInMonth, see monthly every N - months. This will work: - - ULONG monthOfYearInMinutes = 0; - - for( int i = DateTime::cJanuary; i < schedule-=GetMonth(); i++) - - { - - monthOfYearInMinutes += DateTime::GetDaysInMonth( i) * 24 * 60; - - } - - - - 14 ULONG 12: recurrence interval in months. Naturally, 12. - - 18 ULONG 0 for all events and non-regenerating recurring tasks. 1 for - regenerating tasks. - - 22 ULONG D: day of month the event recurs on. See monthly every N months on - day D. - - Yearly on the Xth Y of month M: 10 ULONG M (sort of): See yearly on day D of - month M. - - 14 ULONG 12: recurrence interval in months. Naturally, 12. - - 18 ULONG Constant: 0 - - 22 ULONG Y: see monthly every N months on the Xth Y. - - 26 ULONG X: see monthly every N months on the Xth Y. - - After these recurrence-type-specific values, the offsets will change - depending on the type. For every type except daily every N days, the offsets - will grow by at least 4. For those types using the Xth Y, the offsets will - grow by an additional 4, for a total of 8. The offsets for the rest of these - values will be given for the most basic case, daily every N days, i.e. - without any growth. Adjust as necessary. Also, the presence of exceptions - will change the offsets following the exception data by a variable number of - bytes, so the offsets given in the table are accurate only for those - recurrence patterns without any exceptions. - - - 22 UCHAR Type of pattern termination: 0x21 for terminating on a given date, 0x22 for terminating - after a given number of recurrences, or 0x23 for never terminating - (recurring infinitely) - - 23 UCHARx3 Constant: { 0x20, 0x00, 0x00} - - 26 ULONG Number of occurrences in pattern: 0 for infinite recurrence, - otherwise supply the value, even if it terminates on a given date, not after - a given number - - 30 ULONG Constant: 0 - - 34 ULONG Number of exceptions to pattern (i.e. deleted or changed - occurrences) - - .... ULONGxN Base date of each exception, given in hundreds of nanoseconds - since 1601, so see below to turn them into a comprehensible format. The base - date of an exception is the date (and only the date-- not the time) the - exception would have occurred on in the pattern. They must occur in - ascending order. - - 38 ULONG Number of changed exceptions (i.e. total number of exceptions - - number of deleted exceptions): if there are changed exceptions, again, more - data will be needed, but that will wait - - .... ULONGxN Start date (and only the date-- not the time) of each changed - exception, i.e. the exceptions which aren't deleted. These must also occur - in ascending order. If all of the exceptions are deleted, this data will be - absent. If present, they will be in the format above. Any dates that are in - the first list but not in the second are exceptions that have been deleted - (i.e. the difference between the two sets). Note that this is the start date - (including time), not the base date. Given that the values are unordered and - that they can't be matched up against the previous list in this iteration of - the recurrence data (they could in previous ones), it is very difficult to - tell which exceptions are deleted and which are changed. Fortunately, for - this new format, the base dates are given on the attachment representing the - changed exception (described below), so you can simply ignore this list of - changed exceptions. Just create a list of exceptions from the previous list - and assume they're all deleted unless you encounter an attachment with a - matching base date later on. - - 42 ULONG Start date of pattern given in hundreds of nanoseconds since 1601; - see below for an explanation. - - 46 ULONG End date of pattern: see start date of pattern - - 50 ULONG Constant: { 0x06, 0x30, 0x00, 0x00} - - NOTE: I find the following 8-byte sequence of bytes to be very useful for - orienting myself when looking at the raw data. If you can find { 0x06, 0x30, - 0x00, 0x00, 0x08, 0x30, 0x00, 0x00}, you can use these tables to work either - forwards or backwards to find the data you need. The sequence sort of - delineates certain critical exception-related data and delineates the - exceptions themselves from the rest of the data and is relatively easy to - find. If you're going to be meddling in here a lot, I suggest making a - friend of ol' 0x00003006. - - 54 UCHAR This number is some kind of version indicator. Use 0x08 for Outlook - 2003. I believe 0x06 is Outlook 2000 and possibly 98, while 0x07 is Outlook - XP. This number must be consistent with the features of the data structure - generated by the version of Outlook indicated thereby-- there are subtle - differences between the structures, and, if the version doesn't match the - data, Outlook will sometimes failto read the structure. - - 55 UCHARx3 Constant: { 0x30, 0x00, 0x00} - - 58 ULONG Start time of occurrence in minutes: e.g. 0 for midnight or 720 for - 12 PM - - 62 ULONG End time of occurrence in minutes: i.e. start time + duration, e.g. - 900 for an event that starts at 12 PM and ends at 3PM - - Exception Data 66 USHORT Number of changed exceptions: essentially a check - on the prior occurrence of this value; should be equivalent. - - NOTE: The following structure will occur N many times (where N = number of - changed exceptions), and each structure can be of variable length. - - .... ULONG Start date of changed exception given in hundreds of nanoseconds - since 1601 - - .... ULONG End date of changed exception given in hundreds of nanoseconds - since 1601 - - .... ULONG This is a value I don't clearly understand. It seems to be some - kind of archival value that matches the start time most of the time, but - will lag behind when the start time is changed and then match up again under - certain conditions later. In any case, setting to the same value as the - start time seems to work just fine (more information on this value would be - appreciated). - - .... USHORT Bitmask of changes to the exception (see below). This will be 0 - if the only changes to the exception were to its start or end time. - - .... ULONGxN Numeric values (e.g. label or minutes to remind before the - event) changed in the exception. These will occur in the order of their - corresponding bits (see below). If no numeric values were changed, then - these values will be absent. - - NOTE: The following three values constitute a single sub-structure that will - occur N many times, where N is the number of strings that are changed in the - exception. Since there are at most 2 string values that can be excepted - (i.e. subject [or description], and location), there can at most be two of - these, but there may be none. - - .... USHORT Length of changed string value with NULL character - - .... USHORT Length of changed string value without NULL character (i.e. - previous value - 1) - - .... CHARxN Changed string value (without NULL terminator) - - Unicode Data NOTE: If a string value was changed on an exception, those - changed string values will reappear here in Unicode format after 8 bytes of - NULL padding (possibly a Unicode terminator?). For each exception with a - changed string value, there will be an identifier, followed by the changed - strings in Unicode. The strings will occur in the order of their - corresponding bits (see below). E.g., if both subject and location were - changed in the exception, there would be the 3-ULONG identifier, then the - length of the subject, then the subject, then the length of the location, - then the location. - - 70 ULONGx2 Constant: { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}. This - padding serves as a barrier between the older data structure and the - appended Unicode data. This is the same sequence as the Unicode terminator, - but I'm not sure whether that's its identity or not. - - .... ULONGx3 These are the three times used to identify the exception above: - start date, end date, and repeated start date. These should be the same as - they were above. - - .... USHORT Length of changed string value without NULL character. This is - given as count of WCHARs, so it should be identical to the value above. - - .... WCHARxN Changed string value in Unicode (without NULL terminator) - - Terminator ... ULONGxN Constant: { 0x00, 0x00, 0x00, 0x00}. 4 bytes of NULL - padding per changed exception. If there were no changed exceptions, all - you'll need is the final terminator below. - - .... ULONGx2 Constant: { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}. - - */ diff --git a/sources/backend/zarafa/mapi/class.taskrecurrence.php b/sources/backend/zarafa/mapi/class.taskrecurrence.php deleted file mode 100644 index 9d92c61..0000000 --- a/sources/backend/zarafa/mapi/class.taskrecurrence.php +++ /dev/null @@ -1,463 +0,0 @@ -. - * - */ - - - require_once("backend/zarafa/mapi/class.baserecurrence.php"); - - class TaskRecurrence extends BaseRecurrence - { - /** - * Timezone info which is always false for task - */ - var $tz = false; - - function TaskRecurrence($store, $message) - { - $this->store = $store; - $this->message = $message; - - $properties = array(); - $properties["entryid"] = PR_ENTRYID; - $properties["parent_entryid"] = PR_PARENT_ENTRYID; - $properties["icon_index"] = PR_ICON_INDEX; - $properties["message_class"] = PR_MESSAGE_CLASS; - $properties["message_flags"] = PR_MESSAGE_FLAGS; - $properties["subject"] = PR_SUBJECT; - $properties["importance"] = PR_IMPORTANCE; - $properties["sensitivity"] = PR_SENSITIVITY; - $properties["last_modification_time"] = PR_LAST_MODIFICATION_TIME; - $properties["status"] = "PT_LONG:PSETID_Task:0x8101"; - $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102"; - $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104"; - $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105"; - $properties["reset_reminder"] = "PT_BOOLEAN:PSETID_Task:0x8107"; - $properties["dead_occurrence"] = "PT_BOOLEAN:PSETID_Task:0x8109"; - $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f"; - $properties["recurring_data"] = "PT_BINARY:PSETID_Task:0x8116"; - $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110"; - $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111"; - $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c"; - $properties["task_f_creator"] = "PT_BOOLEAN:PSETID_Task:0x811e"; - $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f"; - $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126"; - - $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501"; - $properties["reminder_time"] = "PT_SYSTIME:PSETID_Common:0x8502"; - $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503"; - - $properties["private"] = "PT_BOOLEAN:PSETID_Common:0x8506"; - $properties["contacts"] = "PT_MV_STRING8:PSETID_Common:0x853a"; - $properties["contacts_string"] = "PT_STRING8:PSETID_Common:0x8586"; - $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords"; - - $properties["commonstart"] = "PT_SYSTIME:PSETID_Common:0x8516"; - $properties["commonend"] = "PT_SYSTIME:PSETID_Common:0x8517"; - $properties["commonassign"] = "PT_LONG:PSETID_Common:0x8518"; - $properties["flagdueby"] = "PT_SYSTIME:PSETID_Common:0x8560"; - $properties["side_effects"] = "PT_LONG:PSETID_Common:0x8510"; - $properties["reminder"] = "PT_BOOLEAN:PSETID_Common:0x8503"; - $properties["reminder_minutes"] = "PT_LONG:PSETID_Common:0x8501"; - - $this->proptags = getPropIdsFromStrings($store, $properties); - - parent::BaseRecurrence($store, $message, $properties); - } - - /** - * Function which saves recurrence and also regenerates task if necessary. - *@param array $recur new recurrence properties - *@return array of properties of regenerated task else false - */ - function setRecurrence(&$recur) - { - $this->recur = $recur; - $this->action =& $recur; - - if(!isset($this->recur["changed_occurences"])) - $this->recur["changed_occurences"] = Array(); - - if(!isset($this->recur["deleted_occurences"])) - $this->recur["deleted_occurences"] = Array(); - - if (!isset($this->recur['startocc'])) $this->recur['startocc'] = 0; - if (!isset($this->recur['endocc'])) $this->recur['endocc'] = 0; - - // Save recurrence because we need proper startrecurrdate and endrecurrdate - $this->saveRecurrence(); - - // Update $this->recur with proper startrecurrdate and endrecurrdate updated after saveing recurrence - $msgProps = mapi_getprops($this->message, array($this->proptags['recurring_data'])); - $recurring_data = $this->parseRecurrence($msgProps[$this->proptags['recurring_data']]); - foreach($recurring_data as $key => $value) { - $this->recur[$key] = $value; - } - - $this->setFirstOccurrence(); - - // Let's see if next occurrence has to be generated - return $this->moveToNextOccurrence(); - } - - /** - * Sets task object to first occurrence if startdate/duedate of task object is different from first occurrence - */ - function setFirstOccurrence() - { - // Check if it is already the first occurrence - if($this->action['start'] == $this->recur["start"]){ - return; - }else{ - $items = $this->getNextOccurrence(); - - $props = array(); - $props[$this->proptags['startdate']] = $items[$this->proptags['startdate']]; - $props[$this->proptags['commonstart']] = $items[$this->proptags['startdate']]; - - $props[$this->proptags['duedate']] = $items[$this->proptags['duedate']]; - $props[$this->proptags['commonend']] = $items[$this->proptags['duedate']]; - - mapi_setprops($this->message, $props); - } - } - - /** - * Function which creates new task as current occurrence and moves the - * existing task to next occurrence. - * - *@param array $recur $action from client - *@return boolean if moving to next occurrence succeed then it returns - * properties of either newly created task or existing task ELSE - * false because that was last occurrence - */ - function moveToNextOccurrence() - { - $result = false; - /** - * Every recurring task should have a 'duedate'. If a recurring task is created with no start/end date - * then we create first two occurrence separately and for first occurrence recurrence has ended. - */ - if ((empty($this->action['startdate']) && empty($this->action['duedate'])) - || ($this->action['complete'] == 1) || (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence'])){ - - $nextOccurrence = $this->getNextOccurrence(); - $result = mapi_getprops($this->message, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID)); - - $props = array(); - if ($nextOccurrence) { - if (!isset($this->action['deleteOccurrence'])) { - // Create current occurrence as separate task - $result = $this->regenerateTask($this->action['complete']); - } - - // Set reminder for next occurrence - $this->setReminder($nextOccurrence); - - // Update properties for next occurrence - $this->action['duedate'] = $props[$this->proptags['duedate']] = $nextOccurrence[$this->proptags['duedate']]; - $this->action['commonend'] = $props[$this->proptags['commonend']] = $nextOccurrence[$this->proptags['duedate']]; - - $this->action['startdate'] = $props[$this->proptags['startdate']] = $nextOccurrence[$this->proptags['startdate']]; - $this->action['commonstart'] = $props[$this->proptags['commonstart']] = $nextOccurrence[$this->proptags['startdate']]; - - // If current task as been mark as 'Complete' then next occurrence should be uncomplete. - if (isset($this->action['complete']) && $this->action['complete'] == 1) { - $this->action['status'] = $props[$this->proptags["status"]] = olTaskNotStarted; - $this->action['complete'] = $props[$this->proptags["complete"]] = false; - $this->action['percent_complete'] = $props[$this->proptags["percent_complete"]] = 0; - } - - $props[$this->proptags["dead_occurrence"]] = false; - } else { - if (isset($this->action['deleteOccurrence']) && $this->action['deleteOccurrence']) - return false; - - // Didn't get next occurrence, probably this is the last one, so recurrence ends here - $props[$this->proptags["dead_occurrence"]] = true; - $props[$this->proptags["datecompleted"]] = $this->action['datecompleted']; - $props[$this->proptags["task_f_creator"]] = true; - - //OL props - $props[$this->proptags["side_effects"]] = 1296; - $props[$this->proptags["icon_index"]] = 1280; - } - - mapi_setprops($this->message, $props); - } - - return $result; - } - - /** - * Function which return properties of next occurrence - *@return array startdate/enddate of next occurrence - */ - function getNextOccurrence() - { - if ($this->recur) { - $items = array(); - - //@TODO: fix start of range - $start = isset($this->messageprops[$this->proptags["duedate"]]) ? $this->messageprops[$this->proptags["duedate"]] : $this->action['start']; - $dayend = ($this->recur['term'] == 0x23) ? 0x7fffffff : $this->dayStartOf($this->recur["end"]); - - // Fix recur object - $this->recur['startocc'] = 0; - $this->recur['endocc'] = 0; - - // Retrieve next occurrence - $items = $this->getItems($start, $dayend, 1); - - return !empty($items) ? $items[0] : false; - } - } - - /** - * Function which clones current occurrence and sets appropriate properties. - * The original recurring item is moved to next occurrence. - *@param boolean $markComplete true if existing occurrence has to be mark complete else false. - */ - function regenerateTask($markComplete) - { - // Get all properties - $taskItemProps = mapi_getprops($this->message); - - if (isset($this->action["subject"])) $taskItemProps[$this->proptags["subject"]] = $this->action["subject"]; - if (isset($this->action["importance"])) $taskItemProps[$this->proptags["importance"]] = $this->action["importance"]; - if (isset($this->action["startdate"])) { - $taskItemProps[$this->proptags["startdate"]] = $this->action["startdate"]; - $taskItemProps[$this->proptags["commonstart"]] = $this->action["startdate"]; - } - if (isset($this->action["duedate"])) { - $taskItemProps[$this->proptags["duedate"]] = $this->action["duedate"]; - $taskItemProps[$this->proptags["commonend"]] = $this->action["duedate"]; - } - - $folder = mapi_msgstore_openentry($this->store, $taskItemProps[PR_PARENT_ENTRYID]); - $newMessage = mapi_folder_createmessage($folder); - - $taskItemProps[$this->proptags["status"]] = $markComplete ? olTaskComplete : olTaskNotStarted; - $taskItemProps[$this->proptags["complete"]] = $markComplete; - $taskItemProps[$this->proptags["percent_complete"]] = $markComplete ? 1 : 0; - - // This occurrence has been marked as 'Complete' so disable reminder - if ($markComplete) { - $taskItemProps[$this->proptags["reset_reminder"]] = false; - $taskItemProps[$this->proptags["reminder"]] = false; - $taskItemProps[$this->proptags["datecompleted"]] = $this->action["datecompleted"]; - - unset($this->action[$this->proptags['datecompleted']]); - } - - // Recurrence ends for this item - $taskItemProps[$this->proptags["dead_occurrence"]] = true; - $taskItemProps[$this->proptags["task_f_creator"]] = true; - - //OL props - $taskItemProps[$this->proptags["side_effects"]] = 1296; - $taskItemProps[$this->proptags["icon_index"]] = 1280; - - // Copy recipients - $recipienttable = mapi_message_getrecipienttable($this->message); - $recipients = mapi_table_queryallrows($recipienttable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID)); - - $copy_to_recipientTable = mapi_message_getrecipienttable($newMessage); - $copy_to_recipientRows = mapi_table_queryallrows($copy_to_recipientTable, array(PR_ROWID)); - foreach($copy_to_recipientRows as $recipient) { - mapi_message_modifyrecipients($newMessage, MODRECIP_REMOVE, array($recipient)); - } - mapi_message_modifyrecipients($newMessage, MODRECIP_ADD, $recipients); - - // Copy attachments - $attachmentTable = mapi_message_getattachmenttable($this->message); - if($attachmentTable) { - $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD)); - - foreach($attachments as $attach_props){ - $attach_old = mapi_message_openattach($this->message, (int) $attach_props[PR_ATTACH_NUM]); - $attach_newResourceMsg = mapi_message_createattach($newMessage); - - mapi_copyto($attach_old, array(), array(), $attach_newResourceMsg, 0); - mapi_savechanges($attach_newResourceMsg); - } - } - - mapi_setprops($newMessage, $taskItemProps); - mapi_savechanges($newMessage); - - // Update body of original message - $msgbody = mapi_message_openproperty($this->message, PR_BODY); - $msgbody = trim($this->windows1252_to_utf8($msgbody), "\0"); - $separator = "------------\r\n"; - - if (!empty($msgbody) && strrpos($msgbody, $separator) === false) { - $msgbody = $separator . $msgbody; - $stream = mapi_openpropertytostream($this->message, PR_BODY, MAPI_CREATE | MAPI_MODIFY); - mapi_stream_setsize($stream, strlen($msgbody)); - mapi_stream_write($stream, $msgbody); - mapi_stream_commit($stream); - } - - // We need these properties to notify client - return mapi_getprops($newMessage, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID)); - } - - /** - * processOccurrenceItem, adds an item to a list of occurrences, but only if the - * resulting occurrence starts or ends in the interval <$start, $end> - * @param array $items reference to the array to be added to - * @param date $start start of timeframe in GMT TIME - * @param date $end end of timeframe in GMT TIME - * @param date $basedate (hour/sec/min assumed to be 00:00:00) in LOCAL TIME OF THE OCCURRENCE - */ - function processOccurrenceItem(&$items, $start, $end, $now) - { - if ($now > $start) { - $newItem = array(); - $newItem[$this->proptags['startdate']] = $now; - - // If startdate and enddate are set on task, then slide enddate according to duration - if (isset($this->messageprops[$this->proptags["startdate"]]) && isset($this->messageprops[$this->proptags["duedate"]])) { - $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']] + ($this->messageprops[$this->proptags["duedate"]] - $this->messageprops[$this->proptags["startdate"]]); - } else { - $newItem[$this->proptags['duedate']] = $newItem[$this->proptags['startdate']]; - } - - $items[] = $newItem; - } - } - - /** - * Function which marks existing occurrence to 'Complete' - *@param array $recur array action from client - *@return array of properties of regenerated task else false - */ - function markOccurrenceComplete(&$recur) - { - // Fix timezone object - $this->tz = false; - $this->action =& $recur; - $dead_occurrence = isset($this->messageprops[$this->proptags['dead_occurrence']]) ? $this->messageprops[$this->proptags['dead_occurrence']] : false; - - if (!$dead_occurrence) { - return $this->moveToNextOccurrence(); - } - - return false; - } - - /** - * Function which sets reminder on recurring task after existing occurrence has been deleted or marked complete. - *@param array $nextOccurrence properties of next occurrence - */ - function setReminder($nextOccurrence) - { - $props = array(); - if ($nextOccurrence) { - // Check if reminder is reset. Default is 'false' - $reset_reminder = isset($this->messageprops[$this->proptags['reset_reminder']]) ? $this->messageprops[$this->proptags['reset_reminder']] : false; - $reminder = $this->messageprops[$this->proptags['reminder']]; - - // Either reminder was already set OR reminder was set but was dismissed bty user - if ($reminder || $reset_reminder) { - // Reminder can be set at any time either before or after the duedate, so get duration between the reminder time and duedate - $reminder_time = isset($this->messageprops[$this->proptags['reminder_time']]) ? $this->messageprops[$this->proptags['reminder_time']] : 0; - $reminder_difference = isset($this->messageprops[$this->proptags['duedate']]) ? $this->messageprops[$this->proptags['duedate']] : 0; - $reminder_difference = $reminder_difference - $reminder_time; - - // Apply duration to next calculated duedate - $next_reminder_time = $nextOccurrence[$this->proptags['duedate']] - $reminder_difference; - - $props[$this->proptags['reminder_time']] = $next_reminder_time; - $props[$this->proptags['flagdueby']] = $next_reminder_time; - $this->action['reminder'] = $props[$this->proptags['reminder']] = true; - } - } else { - // Didn't get next occurrence, probably this is the last occurrence - $props[$this->proptags['reminder']] = false; - $props[$this->proptags['reset_reminder']] = false; - } - - if (!empty($props)) - mapi_setprops($this->message, $props); - } - - /** - * Function which recurring task to next occurrence. - * It simply doesn't regenerate task - @param array $action - */ - function deleteOccurrence($action) - { - $this->tz = false; - $this->action = $action; - $result = $this->moveToNextOccurrence(); - - mapi_savechanges($this->message); - - return $result; - } - - /** - * Convert from windows-1252 encoded string to UTF-8 string - * - * The same conversion rules as utf8_to_windows1252 apply. - * - * @param string $string the Windows-1252 string to convert - * @return string UTF-8 representation of the string - */ - function windows1252_to_utf8($string) - { - if (function_exists("iconv")){ - return iconv("Windows-1252", "UTF-8//TRANSLIT", $string); - }else{ - return utf8_encode($string); // no euro support here - } - } - } diff --git a/sources/backend/zarafa/mapi/class.taskrequest.php b/sources/backend/zarafa/mapi/class.taskrequest.php deleted file mode 100644 index 04b4ecc..0000000 --- a/sources/backend/zarafa/mapi/class.taskrequest.php +++ /dev/null @@ -1,1035 +0,0 @@ -. - * - */ - - - /* - * In general - * - * This class never actually modifies a task item unless we receive a task request update. This means - * that setting all the properties to make the task item itself behave like a task request is up to the - * caller. - * - * The only exception to this is the generation of the TaskGlobalObjId, the unique identifier identifying - * this task request to both the organizer and the assignee. The globalobjectid is generated when the - * task request is sent via sendTaskRequest. - */ - - /* The TaskMode value is only used for the IPM.TaskRequest items. It must 0 (tdmtNothing) on IPM.Task items. - * - * It is used to indicate the type of change that is being carried in the IPM.TaskRequest item (although this - * information seems redundant due to that information already being available in PR_MESSAGE_CLASS). - */ - define('tdmtNothing', 0); // Value in IPM.Task items - define('tdmtTaskReq', 1); // Assigner -> Assignee - define('tdmtTaskAcc', 2); // Assignee -> Assigner - define('tdmtTaskDec', 3); // Assignee -> Assigner - define('tdmtTaskUpd', 4); // Assignee -> Assigner - define('tdmtTaskSELF', 5); // Assigner -> Assigner (?) - - /* The TaskHistory is used to show the last action on the task on both the assigner and the assignee's side. - * - * It is used in combination with 'AssignedTime' and 'tasklastdelegate' or 'tasklastuser' to show the information - * at the top of the task request in the format 'Accepted by on 01-01-2010 11:00'. - */ - define('thNone', 0); - define('thAccepted', 1); // Set by assignee - define('thDeclined', 2); // Set by assignee - define('thUpdated', 3); // Set by assignee - define('thDueDateChanged', 4); - define('thAssigned', 5); // Set by assigner - - /* The TaskState value is used to differentiate the version of a task in the assigner's folder and the version in the - * assignee's folder. The buttons shown depend on this and the 'taskaccepted' boolean (for the assignee) - */ - define('tdsNOM', 0); // Got a response to a deleted task, and re-created the task for the assigner - define('tdsOWNNEW', 1); // Not assigned - define('tdsOWN', 2); // Assignee version - define('tdsACC', 3); // Assigner version - define('tdsDEC', 4); // Assigner version, but assignee declined - - /* The delegationstate is used for the assigner to indicate state - */ - define('olTaskNotDelegated', 0); - define('olTaskDelegationUnknown', 1); // After sending req - define('olTaskDelegationAccepted', 2); // After receiving accept - define('olTaskDelegationDeclined', 3); // After receiving decline - - /* The task ownership indicates the role of the current user relative to the task. - */ - define('olNewTask', 0); - define('olDelegatedTask', 1); // Task has been assigned - define('olOwnTask', 2); // Task owned - - /* taskmultrecips indicates whether the task request sent or received has multiple assignees or not. - */ - define('tmrNone', 0); - define('tmrSent', 1); // Task has been sent to multiple assignee - define('tmrReceived', 2); // Task Request received has multiple assignee - - class TaskRequest { - - // All recipient properties - var $recipprops = Array(PR_ENTRYID, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_ENTRYID, PR_RECIPIENT_TYPE, PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO, PR_RECIPIENT_DISPLAY_NAME, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TRACKSTATUS_TIME, PR_RECIPIENT_FLAGS, PR_ROWID, PR_SEARCH_KEY); - - /* Constructor - * - * Constructs a TaskRequest object for the specified message. This can be either the task request - * message itself (in the inbox) or the task in the tasks folder, depending on the action to be performed. - * - * As a general rule, the object message passed is the object 'in view' when the user performs one of the - * actions in this class. - * - * @param $store store MAPI Store in which $message resides. This is also the store where the tasks folder is assumed to be in - * @param $message message MAPI Message to which the task request referes (can be an email or a task) - * @param $session session MAPI Session which is used to open tasks folders for delegated task requests or responses - */ - function TaskRequest($store, $message, $session) { - $this->store = $store; - $this->message = $message; - $this->session = $session; - - $properties["owner"] = "PT_STRING8:PSETID_Task:0x811f"; - $properties["updatecount"] = "PT_LONG:PSETID_Task:0x8112"; - $properties["taskstate"] = "PT_LONG:PSETID_Task:0x8113"; - $properties["taskmultrecips"] = "PT_LONG:PSETID_Task:0x8120"; - $properties["taskupdates"] = "PT_BOOLEAN:PSETID_Task:0x811b"; - $properties["tasksoc"] = "PT_BOOLEAN:PSETID_Task:0x8119"; - $properties["taskhistory"] = "PT_LONG:PSETID_Task:0x811a"; - $properties["taskmode"] = "PT_LONG:PSETID_Common:0x8518"; - $properties["taskglobalobjid"] = "PT_BINARY:PSETID_Common:0x8519"; - $properties["complete"] = "PT_BOOLEAN:PSETID_Common:0x811c"; - $properties["assignedtime"] = "PT_SYSTIME:PSETID_Task:0x8115"; - $properties["taskfcreator"] = "PT_BOOLEAN:PSETID_Task:0x0x811e"; - $properties["tasklastuser"] = "PT_STRING8:PSETID_Task:0x8122"; - $properties["tasklastdelegate"] = "PT_STRING8:PSETID_Task:0x8125"; - $properties["taskaccepted"] = "PT_BOOLEAN:PSETID_Task:0x8108"; - $properties["delegationstate"] = "PT_LONG:PSETID_Task:0x812a"; - $properties["ownership"] = "PT_LONG:PSETID_Task:0x8129"; - - $properties["complete"] = "PT_BOOLEAN:PSETID_Task:0x811c"; - $properties["datecompleted"] = "PT_SYSTIME:PSETID_Task:0x810f"; - $properties["recurring"] = "PT_BOOLEAN:PSETID_Task:0x8126"; - $properties["startdate"] = "PT_SYSTIME:PSETID_Task:0x8104"; - $properties["duedate"] = "PT_SYSTIME:PSETID_Task:0x8105"; - $properties["status"] = "PT_LONG:PSETID_Task:0x8101"; - $properties["percent_complete"] = "PT_DOUBLE:PSETID_Task:0x8102"; - $properties["totalwork"] = "PT_LONG:PSETID_Task:0x8111"; - $properties["actualwork"] = "PT_LONG:PSETID_Task:0x8110"; - $properties["categories"] = "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords"; - $properties["companies"] = "PT_MV_STRING8:PSETID_Common:0x8539"; - $properties["mileage"] = "PT_STRING8:PSETID_Common:0x8534"; - $properties["billinginformation"] = "PT_STRING8:PSETID_Common:0x8535"; - - $this->props = getPropIdsFromStrings($store, $properties); - } - - // General functions - - /* Return TRUE if the item is a task request message - */ - function isTaskRequest() - { - $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS)); - - if(isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] == "IPM.TaskRequest") { - return true; - } - } - - /* Return TRUE if the item is a task response message - */ - function isTaskRequestResponse() { - $props = mapi_getprops($this->message, Array(PR_MESSAGE_CLASS)); - - if(isset($props[PR_MESSAGE_CLASS]) && strpos($props[PR_MESSAGE_CLASS], "IPM.TaskRequest.") === 0) { - return true; - } - } - - /* - * Gets the task associated with an IPM.TaskRequest message - * - * If the task does not exist yet, it is created, using the attachment object in the - * task request item. - */ - function getAssociatedTask($create) - { - $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS, $this->props['taskglobalobjid'])); - - if($props[PR_MESSAGE_CLASS] == "IPM.Task") - return $this->message; // Message itself is task, so return that - - $tfolder = $this->getDefaultTasksFolder(); - $globalobjid = $props[$this->props['taskglobalobjid']]; - - // Find the task by looking for the taskglobalobjid - $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid)); - - $contents = mapi_folder_getcontentstable($tfolder); - - $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID), $restriction); - - if(empty($rows)) { - // None found, create one if possible - if(!$create) - return false; - - $task = mapi_folder_createmessage($tfolder); - - $sub = $this->getEmbeddedTask($this->message); - mapi_copyto($sub, array(), array(), $task); - - // Copy sender information from the e-mail - $senderprops = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_SEARCH_KEY)); - mapi_setprops($task, $senderprops); - - $senderprops = mapi_getprops($this->message, array(PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENDER_ENTRYID, PR_SENDER_ADDRTYPE, PR_SENDER_SEARCH_KEY)); - mapi_setprops($task, $senderprops); - - } else { - // If there are multiple, just use the first - $entryid = $rows[0][PR_ENTRYID]; - - $store = $this->getTaskFolderStore(); - $task = mapi_msgstore_openentry($store, $entryid); - } - - return $task; - } - - - - // Organizer functions (called by the organizer) - - /* Processes a task request response, which can be any of the following: - * - Task accept (task history is marked as accepted) - * - Task decline (task history is marked as declined) - * - Task update (updates completion %, etc) - */ - function processTaskResponse() { - $messageprops = mapi_getprops($this->message, array(PR_PROCESSED)); - if(isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED]) - return true; - - // Get the task for this response - $task = $this->getAssociatedTask(false); - - if(!$task) { - // Got a response for a task that has been deleted, create a new one and mark it as such - $task = $this->getAssociatedTask(true); - - // tdsNOM indicates a task request that had gone missing - mapi_setprops($task, array($this->props['taskstate'] => tdsNOM )); - } - - // Get the embedded task information and copy it into our task - $sub = $this->getEmbeddedTask($this->message); - mapi_copyto($sub, array(), array($this->props['taskstate'], $this->props['taskhistory'], $this->props['taskmode'], $this->props['taskfcreator']), $task); - - $props = mapi_getprops($this->message, array(PR_MESSAGE_CLASS)); - - // Set correct taskmode and taskhistory depending on response type - switch($props[PR_MESSAGE_CLASS]) { - case 'IPM.TaskRequest.Accept': - $taskhistory = thAccepted; - $taskstate = tdsACC; - $delegationstate = olTaskDelegationAccepted; - break; - case 'IPM.TaskRequest.Decline': - $taskhistory = thDeclined; - $taskstate = tdsDEC; - $delegationstate = olTaskDelegationDeclined; - break; - case 'IPM.TaskRequest.Update': - $taskhistory = thUpdated; - $taskstate = tdsACC; // Doesn't actually change anything - $delegationstate = olTaskDelegationAccepted; - break; - } - - // Update taskstate (what the task looks like) and task history (last action done by the assignee) - mapi_setprops($task, array($this->props['taskhistory'] => $taskhistory, $this->props['taskstate'] => $taskstate, $this->props['delegationstate'] => $delegationstate, $this->props['ownership'] => olDelegatedTask)); - - mapi_setprops($this->message, array(PR_PROCESSED => true)); - mapi_savechanges($task); - - return true; - } - - /* Create a new message in the current user's outbox and submit it - * - * Takes the task passed in the constructor as the task to be sent; recipient should - * be pre-existing. The task request will be sent to all recipients. - */ - function sendTaskRequest($prefix) { - // Generate a TaskGlobalObjectId - $taskid = $this->createTGOID(); - $messageprops = mapi_getprops($this->message, array(PR_SUBJECT)); - - // Set properties on Task Request - mapi_setprops($this->message, array( - $this->props['taskglobalobjid'] => $taskid, /* our new taskglobalobjid */ - $this->props['taskstate'] => tdsACC, /* state for our outgoing request */ - $this->props['taskmode'] => tdmtNothing, /* we're not sending a change */ - $this->props['updatecount'] => 2, /* version 2 (no idea) */ - $this->props['delegationstate'] => olTaskDelegationUnknown, /* no reply yet */ - $this->props['ownership'] => olDelegatedTask, /* Task has been assigned */ - $this->props['taskhistory'] => thAssigned, /* Task has been assigned */ - PR_ICON_INDEX => 1283 /* Task request icon*/ - )); - $this->setLastUser(); - $this->setOwnerForAssignor(); - mapi_savechanges($this->message); - - // Create outgoing task request message - $outgoing = $this->createOutgoingMessage(); - // No need to copy attachments as task will be attached as embedded message. - mapi_copyto($this->message, array(), array(PR_MESSAGE_ATTACHMENTS), $outgoing); - - // Make it a task request, and put it in sent items after it is sent - mapi_setprops($outgoing, array( - PR_MESSAGE_CLASS => "IPM.TaskRequest", /* class is task request */ - $this->props['taskstate'] => tdsOWNNEW, /* for the recipient the task is new */ - $this->props['taskmode'] => tdmtTaskReq, /* for the recipient it's a request */ - $this->props['updatecount'] => 1, /* version 2 is in the attachment */ - PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT], - PR_ICON_INDEX => 0xFFFFFFFF, /* show assigned icon */ - )); - - // Set Body - $body = $this->getBody(); - $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY); - mapi_stream_setsize($stream, strlen($body)); - mapi_stream_write($stream, $body); - mapi_stream_commit($stream); - - $attach = mapi_message_createattach($outgoing); - mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT])); - - $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_MODIFY | MAPI_CREATE); - - mapi_copyto($this->message, array(), array(), $sub); - mapi_savechanges($sub); - - mapi_savechanges($attach); - - mapi_savechanges($outgoing); - mapi_message_submitmessage($outgoing); - return true; - } - - // Assignee functions (called by the assignee) - - /* Update task version counter - * - * Must be called before each update to increase counter - */ - function updateTaskRequest() { - $messageprops = mapi_getprops($this->message, array($this->props['updatecount'])); - - if(isset($messageprops)) { - $messageprops[$this->props['updatecount']]++; - } else { - $messageprops[$this->props['updatecount']] = 1; - } - - mapi_setprops($this->message, $messageprops); - } - - /* Process a task request - * - * Message passed should be an IPM.TaskRequest message. The task request is then processed to create - * the task in the tasks folder if needed. - */ - function processTaskRequest() { - if(!$this->isTaskRequest()) - return false; - - $messageprops = mapi_getprops($this->message, array(PR_PROCESSED)); - if (isset($messageprops[PR_PROCESSED]) && $messageprops[PR_PROCESSED]) - return true; - - $task = $this->getAssociatedTask(true); - $taskProps = mapi_getprops($task, array($this->props['taskmultrecips'])); - - // Set the task state to say that we're the attendee receiving the message, that we have not yet responded and that this message represents no change - $taskProps[$this->props["taskstate"]] = tdsOWN; - $taskProps[$this->props["taskhistory"]] = thAssigned; - $taskProps[$this->props["taskmode"]] = tdmtNothing; - $taskProps[$this->props["taskaccepted"]] = false; - $taskProps[$this->props["taskfcreator"]] = false; - $taskProps[$this->props["ownership"]] = olOwnTask; - $taskProps[$this->props["delegationstate"]] = olTaskNotDelegated; - $taskProps[PR_ICON_INDEX] = 1282; - - // This task was assigned to multiple recips, so set this user as owner - if (isset($taskProps[$this->props['taskmultrecips']]) && $taskProps[$this->props['taskmultrecips']] == tmrSent) { - $loginUserData = $this->retrieveUserData(); - - if ($loginUserData) { - $taskProps[$this->props['owner']] = $loginUserData[PR_DISPLAY_NAME]; - $taskProps[$this->props['taskmultrecips']] = tmrReceived; - } - } - mapi_setprops($task, $taskProps); - - $this->setAssignorInRecipients($task); - - mapi_savechanges($task); - - $taskprops = mapi_getprops($task, array(PR_ENTRYID)); - - mapi_setprops($this->message, array(PR_PROCESSED => true)); - mapi_savechanges($this->message); - - return $taskprops[PR_ENTRYID]; - } - - /* Accept a task request and send the response. - * - * Message passed should be an IPM.Task (eg the task from getAssociatedTask()) - * - * Copies the task to the user's task folder, sets it to accepted, and sends the acceptation - * message back to the organizer. The caller is responsible for removing the message. - * - * @return entryid EntryID of the accepted task - */ - function doAccept($prefix) { - $messageprops = mapi_getprops($this->message, array($this->props['taskstate'])); - - if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) - return false; // Can only accept assignee task - - $this->setLastUser(); - $this->updateTaskRequest(); - - // Set as accepted - mapi_setprops($this->message, array($this->props['taskhistory'] => thAccepted, $this->props['assignedtime'] => time(), $this->props['taskaccepted'] => true, $this->props['delegationstate'] => olTaskNotDelegated)); - - mapi_savechanges($this->message); - - $this->sendResponse(tdmtTaskAcc, $prefix); - - //@TODO: delete received task request from Inbox - return $this->deleteReceivedTR(); - } - - /* Decline a task request and send the response. - * - * Passed message must be a task request message, ie isTaskRequest() must return TRUE. - * - * Sends the decline message back to the organizer. The caller is responsible for removing the message. - * - * @return boolean TRUE on success, FALSE on failure - */ - function doDecline($prefix) { - $messageprops = mapi_getprops($this->message, array($this->props['taskstate'])); - - if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) - return false; // Can only decline assignee task - - $this->setLastUser(); - $this->updateTaskRequest(); - - // Set as declined - mapi_setprops($this->message, array($this->props['taskhistory'] => thDeclined, $this->props['delegationstate'] => olTaskDelegationDeclined)); - - mapi_savechanges($this->message); - - $this->sendResponse(tdmtTaskDec, $prefix); - - return $this->deleteReceivedTR(); - } - - /* Send an update of the task if requested, and send the Status-On-Completion report if complete and requested - * - * If no updates were requested from the organizer, this function does nothing. - * - * @return boolean TRUE if the update succeeded, FALSE otherwise. - */ - function doUpdate($prefix, $prefixComplete) { - $messageprops = mapi_getprops($this->message, array($this->props['taskstate'], PR_SUBJECT)); - - if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) - return false; // Can only update assignee task - - $this->setLastUser(); - $this->updateTaskRequest(); - - // Set as updated - mapi_setprops($this->message, array($this->props['taskhistory'] => thUpdated)); - - mapi_savechanges($this->message); - - $props = mapi_getprops($this->message, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['recurring'], $this->props['complete'])); - if ($props[$this->props['taskupdates']] && !(isset($props[$this->props['recurring']]) && $props[$this->props['recurring']])) - $this->sendResponse(tdmtTaskUpd, $prefix); - - if($props[$this->props['tasksoc']] && $props[$this->props['complete']] ) { - $outgoing = $this->createOutgoingMessage(); - - mapi_setprops($outgoing, array(PR_SUBJECT => $prefixComplete . $messageprops[PR_SUBJECT])); - - $this->setRecipientsForResponse($outgoing, tdmtTaskUpd, true); - $body = $this->getBody(); - $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY); - mapi_stream_setsize($stream, strlen($body)); - mapi_stream_write($stream, $body); - mapi_stream_commit($stream); - - mapi_savechanges($outgoing); - mapi_message_submitmessage($outgoing); - } - } - - // Internal functions - - /* Get the store associated with the task - * - * Normally this will just open the store that the processed message is in. However, if the message is opened - * by a delegate, this function opens the store that the message was delegated from. - */ - function getTaskFolderStore() - { - $ownerentryid = false; - - $rcvdprops = mapi_getprops($this->message, array(PR_RCVD_REPRESENTING_ENTRYID)); - if(isset($rcvdprops[PR_RCVD_REPRESENTING_ENTRYID])) { - $ownerentryid = $rcvdprops; - } - - if(!$ownerentryid) { - $store = $this->store; - } else { - $ab = mapi_openaddressbook($this->session); // seb changed from $session to $this->session - if(!$ab) return false; // manni $ before ab was missing - - $mailuser = mapi_ab_openentry($ab, $ownerentryid); - if(!$mailuser) return false; - - $mailuserprops = mapi_getprops($mailuser, array(PR_EMAIL_ADDRESS)); - if(!isset($mailuserprops[PR_EMAIL_ADDRESS])) return false; - - $storeid = mapi_msgstore_createentryid($this->store, $mailuserprops[PR_EMAIL_ADDRESS]); - - $store = mapi_openmsgstore($this->session, $storeid); - - } - return $store; - } - - /* Open the default task folder for the current user, or the specified user if passed - * - * @param $ownerentryid (Optional)EntryID of user for which we are opening the task folder - */ - function getDefaultTasksFolder() - { - $store = $this->getTaskFolderStore(); - - $inbox = mapi_msgstore_getreceivefolder($store); - $inboxprops = mapi_getprops($inbox, Array(PR_IPM_TASK_ENTRYID)); - if(!isset($inboxprops[PR_IPM_TASK_ENTRYID])) - return false; - - return mapi_msgstore_openentry($store, $inboxprops[PR_IPM_TASK_ENTRYID]); - } - - function getSentReprProps($store) - { - $storeprops = mapi_getprops($store, array(PR_MAILBOX_OWNER_ENTRYID)); - if(!isset($storeprops[PR_MAILBOX_OWNER_ENTRYID])) return false; - - $ab = mapi_openaddressbook($this->session); - $mailuser = mapi_ab_openentry($ab, $storeprops[PR_MAILBOX_OWNER_ENTRYID]); - $mailuserprops = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_DISPLAY_NAME, PR_SEARCH_KEY, PR_ENTRYID)); - - $props = array(); - $props[PR_SENT_REPRESENTING_ADDRTYPE] = $mailuserprops[PR_ADDRTYPE]; - $props[PR_SENT_REPRESENTING_EMAIL_ADDRESS] = $mailuserprops[PR_EMAIL_ADDRESS]; - $props[PR_SENT_REPRESENTING_NAME] = $mailuserprops[PR_DISPLAY_NAME]; - $props[PR_SENT_REPRESENTING_SEARCH_KEY] = $mailuserprops[PR_SEARCH_KEY]; - $props[PR_SENT_REPRESENTING_ENTRYID] = $mailuserprops[PR_ENTRYID]; - - return $props; - } - - /* - * Creates an outgoing message based on the passed message - will set delegate information - * and sentmail folder - */ - function createOutgoingMessage() - { - // Open our default store for this user (that's the only store we can submit in) - $store = $this->getDefaultStore(); - $storeprops = mapi_getprops($store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_SENTMAIL_ENTRYID)); - - $outbox = mapi_msgstore_openentry($store, $storeprops[PR_IPM_OUTBOX_ENTRYID]); - if(!$outbox) return false; - - $outgoing = mapi_folder_createmessage($outbox); - if(!$outgoing) return false; - - // Set SENT_REPRESENTING in case we're sending as a delegate - $ownerstore = $this->getTaskFolderStore(); - $sentreprprops = $this->getSentReprProps($ownerstore); - mapi_setprops($outgoing, $sentreprprops); - - mapi_setprops($outgoing, array(PR_SENTMAIL_ENTRYID => $storeprops[PR_IPM_SENTMAIL_ENTRYID])); - - return $outgoing; - } - - /* - * Send a response message (from assignee back to organizer). - * - * @param $type int Type of response (tdmtTaskAcc, tdmtTaskDec, tdmtTaskUpd); - * @return boolean TRUE on success - */ - function sendResponse($type, $prefix) - { - // Create a message in our outbox - $outgoing = $this->createOutgoingMessage(); - - $messageprops = mapi_getprops($this->message, array(PR_SUBJECT)); - - $attach = mapi_message_createattach($outgoing); - mapi_setprops($attach, array(PR_ATTACH_METHOD => ATTACH_EMBEDDED_MSG, PR_DISPLAY_NAME => $messageprops[PR_SUBJECT], PR_ATTACHMENT_HIDDEN => true)); - $sub = mapi_attach_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY); - - mapi_copyto($this->message, array(), array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_SEARCH_KEY), $outgoing); - mapi_copyto($this->message, array(), array(), $sub); - - if (!$this->setRecipientsForResponse($outgoing, $type)) return false; - - switch($type) { - case tdmtTaskAcc: - $messageclass = "IPM.TaskRequest.Accept"; - break; - case tdmtTaskDec: - $messageclass = "IPM.TaskRequest.Decline"; - break; - case tdmtTaskUpd: - $messageclass = "IPM.TaskRequest.Update"; - break; - }; - - mapi_savechanges($sub); - mapi_savechanges($attach); - - // Set Body - $body = $this->getBody(); - $stream = mapi_openpropertytostream($outgoing, PR_BODY, MAPI_CREATE | MAPI_MODIFY); - mapi_stream_setsize($stream, strlen($body)); - mapi_stream_write($stream, $body); - mapi_stream_commit($stream); - - // Set subject, taskmode, message class, icon index, response time - mapi_setprops($outgoing, array(PR_SUBJECT => $prefix . $messageprops[PR_SUBJECT], - $this->props['taskmode'] => $type, - PR_MESSAGE_CLASS => $messageclass, - PR_ICON_INDEX => 0xFFFFFFFF, - $this->props['assignedtime'] => time())); - - mapi_savechanges($outgoing); - mapi_message_submitmessage($outgoing); - - return true; - } - - function getDefaultStore() - { - $table = mapi_getmsgstorestable($this->session); - $rows = mapi_table_queryallrows($table, array(PR_DEFAULT_STORE, PR_ENTRYID)); - - foreach($rows as $row) { - if($row[PR_DEFAULT_STORE]) - return mapi_openmsgstore($this->session, $row[PR_ENTRYID]); - } - - return false; - } - - /* Creates a new TaskGlobalObjId - * - * Just 16 bytes of random data - */ - function createTGOID() - { - $goid = ""; - for($i=0;$i<16;$i++) { - $goid .= chr(rand(0, 255)); - } - return $goid; - } - - function getEmbeddedTask($message) { - $table = mapi_message_getattachmenttable($message); - $rows = mapi_table_queryallrows($table, array(PR_ATTACH_NUM)); - - // Assume only one attachment - if(empty($rows)) - return false; - - $attach = mapi_message_openattach($message, $rows[0][PR_ATTACH_NUM]); - $message = mapi_openproperty($attach, PR_ATTACH_DATA_OBJ, IID_IMessage, 0, 0); - - return $message; - } - - function setLastUser() { - $delegatestore = $this->getDefaultStore(); - $taskstore = $this->getTaskFolderStore(); - - $delegateprops = mapi_getprops($delegatestore, array(PR_MAILBOX_OWNER_NAME)); - $taskprops = mapi_getprops($taskstore, array(PR_MAILBOX_OWNER_NAME)); - - // The owner of the task - $username = $delegateprops[PR_MAILBOX_OWNER_NAME]; - // This is me (the one calling the script) - $delegate = $taskprops[PR_MAILBOX_OWNER_NAME]; - - mapi_setprops($this->message, array($this->props["tasklastuser"] => $username, $this->props["tasklastdelegate"] => $delegate, $this->props['assignedtime'] => time())); - } - - /** Assignee becomes the owner when a user/assignor assigns any task to someone. Also there can be more than one assignee. - * This function sets assignee as owner in the assignor's copy of task. - */ - function setOwnerForAssignor() - { - $recipTable = mapi_message_getrecipienttable($this->message); - $recips = mapi_table_queryallrows($recipTable, array(PR_DISPLAY_NAME)); - - if (!empty($recips)) { - $owner = array(); - foreach ($recips as $value) { - $owner[] = $value[PR_DISPLAY_NAME]; - } - - $props = array($this->props['owner'] => implode("; ", $owner)); - mapi_setprops($this->message, $props); - } - } - - /** Sets assignor as recipients in assignee's copy of task. - * - * If assignor has requested task updates then the assignor is added as recipient type MAPI_CC. - * - * Also if assignor has request SOC then the assignor is also add as recipient type MAPI_BCC - * - * @param $task message MAPI message which assignee's copy of task - */ - function setAssignorInRecipients($task) - { - $recipTable = mapi_message_getrecipienttable($task); - - // Delete all MAPI_TO recipients - $recips = mapi_table_queryallrows($recipTable, array(PR_ROWID), array(RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => MAPI_TO - ))); - foreach($recips as $recip) - mapi_message_modifyrecipients($task, MODRECIP_REMOVE, array($recip)); - - $recips = array(); - $taskReqProps = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_ADDRTYPE)); - $associatedTaskProps = mapi_getprops($task, array($this->props['taskupdates'], $this->props['tasksoc'], $this->props['taskmultrecips'])); - - // Build assignor info - $assignor = array( PR_ENTRYID => $taskReqProps[PR_SENT_REPRESENTING_ENTRYID], - PR_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME], - PR_EMAIL_ADDRESS => $taskReqProps[PR_SENT_REPRESENTING_EMAIL_ADDRESS], - PR_RECIPIENT_DISPLAY_NAME => $taskReqProps[PR_SENT_REPRESENTING_NAME], - PR_ADDRTYPE => empty($taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE]) ? 'SMTP' : $taskReqProps[PR_SENT_REPRESENTING_ADDRTYPE], - PR_RECIPIENT_FLAGS => recipSendable - ); - - // Assignor has requested task updates, so set him/her as MAPI_CC in recipienttable. - if ((isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['taskupdates']]) - && !(isset($associatedTaskProps[$this->props['taskmultrecips']]) && $associatedTaskProps[$this->props['taskmultrecips']] == tmrReceived)) { - $assignor[PR_RECIPIENT_TYPE] = MAPI_CC; - $recips[] = $assignor; - } - - // Assignor wants to receive an email report when task is mark as 'Complete', so in recipients as MAPI_BCC - if (isset($associatedTaskProps[$this->props['taskupdates']]) && $associatedTaskProps[$this->props['tasksoc']]) { - $assignor[PR_RECIPIENT_TYPE] = MAPI_BCC; - $recips[] = $assignor; - } - - if (!empty($recips)) - mapi_message_modifyrecipients($task, MODRECIP_ADD, $recips); - } - - /** Returns user information who has task request - */ - function retrieveUserData() - { - // get user entryid - $storeProps = mapi_getprops($this->store, array(PR_USER_ENTRYID)); - if (!$storeProps[PR_USER_ENTRYID]) return false; - - $ab = mapi_openaddressbook($this->session); - // open the user entry - $user = mapi_ab_openentry($ab, $storeProps[PR_USER_ENTRYID]); - if (!$user) return false; - - // receive userdata - $userProps = mapi_getprops($user, array(PR_DISPLAY_NAME)); - if (!$userProps[PR_DISPLAY_NAME]) return false; - - return $userProps; - } - - /** Deletes incoming task request from Inbox - * - * @returns array returns PR_ENTRYID, PR_STORE_ENTRYID and PR_PARENT_ENTRYID of the deleted task request - */ - function deleteReceivedTR() - { - $store = $this->getTaskFolderStore(); - $inbox = mapi_msgstore_getreceivefolder($store); - - $storeProps = mapi_getprops($store, array(PR_IPM_WASTEBASKET_ENTRYID)); - $props = mapi_getprops($this->message, array($this->props['taskglobalobjid'])); - $globalobjid = $props[$this->props['taskglobalobjid']]; - - // Find the task by looking for the taskglobalobjid - $restriction = array(RES_PROPERTY, array(RELOP => RELOP_EQ, ULPROPTAG => $this->props['taskglobalobjid'], VALUE => $globalobjid)); - - $contents = mapi_folder_getcontentstable($inbox); - - $rows = mapi_table_queryallrows($contents, array(PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID), $restriction); - - $taskrequest = false; - if(!empty($rows)) { - // If there are multiple, just use the first - $entryid = $rows[0][PR_ENTRYID]; - $wastebasket = mapi_msgstore_openentry($store, $storeProps[PR_IPM_WASTEBASKET_ENTRYID]); - mapi_folder_copymessages($inbox, Array($entryid), $wastebasket, MESSAGE_MOVE); - - return array(PR_ENTRYID => $entryid, PR_PARENT_ENTRYID => $rows[0][PR_PARENT_ENTRYID], PR_STORE_ENTRYID => $rows[0][PR_STORE_ENTRYID]); - } - - return false; - } - - /** Converts already sent task request to normal task - */ - function createUnassignedCopy() - { - mapi_deleteprops($this->message, array($this->props['taskglobalobjid'])); - mapi_setprops($this->message, array($this->props['updatecount'] => 1)); - - // Remove all recipents - $this->deleteAllRecipients($this->message); - } - - /** Sets recipients for the outgoing message according to type of the response. - * - * If it is a task update, then only recipient type MAPI_CC are taken from the task message. - * - * If it is accept/decline response, then PR_SENT_REPRESENTATING_XXXX are taken as recipient. - * - *@param $outgoing MAPI_message outgoing mapi message - *@param $responseType String response type - *@param $sendSOC Boolean true if sending complete response else false. - */ - function setRecipientsForResponse($outgoing, $responseType, $sendSOC = false) - { - // Clear recipients from outgoing msg - $this->deleteAllRecipients($outgoing); - - // If it is a task update then get MAPI_CC recipients which are assignors who has asked for task update. - if ($responseType == tdmtTaskUpd) { - $recipTable = mapi_message_getrecipienttable($this->message); - $recips = mapi_table_queryallrows($recipTable, $this->recipprops, array(RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => PR_RECIPIENT_TYPE, - VALUE => ($sendSOC ? MAPI_BCC : MAPI_CC) - ) - )); - - // No recipients found, return error - if (empty($recips)) - return false; - - foreach($recips as $recip) { - $recip[PR_RECIPIENT_TYPE] = MAPI_TO; // Change recipient type to MAPI_TO - mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip)); - } - return true; - } - - $orgprops = mapi_getprops($this->message, array(PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS, PR_SENT_REPRESENTING_ADDRTYPE, PR_SENT_REPRESENTING_ENTRYID, PR_SUBJECT)); - $recip = array(PR_DISPLAY_NAME => $orgprops[PR_SENT_REPRESENTING_NAME], PR_EMAIL_ADDRESS => $orgprops[PR_SENT_REPRESENTING_EMAIL_ADDRESS], PR_ADDRTYPE => $orgprops[PR_SENT_REPRESENTING_ADDRTYPE], PR_ENTRYID => $orgprops[PR_SENT_REPRESENTING_ENTRYID], PR_RECIPIENT_TYPE => MAPI_TO); - - mapi_message_modifyrecipients($outgoing, MODRECIP_ADD, array($recip)); - - return true; - } - - /** Adds task details to message body and returns body. - * - *@return string contructed body with task details. - */ - function getBody() - { - //@TODO: Fix translations - - $msgProps = mapi_getprops($this->message); - $body = ""; - - if (isset($msgProps[PR_SUBJECT])) $body .= "\n" . _("Subject") . ":\t". $msgProps[PR_SUBJECT]; - if (isset($msgProps[$this->props['startdate']])) $body .= "\n" . _("Start Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['startdate']]); - if (isset($msgProps[$this->props['duedate']])) $body .= "\n" . _("Due Date") . ":\t". strftime(_("%A, %B %d, %Y"),$msgProps[$this->props['duedate']]); - $body .= "\n"; - - if (isset($msgProps[$this->props['status']])) { - $body .= "\n" . _("Status") . ":\t"; - if ($msgProps[$this->props['status']] == 0) $body .= _("Not Started"); - else if ($msgProps[$this->props['status']] == 1) $body .= _("In Progress"); - else if ($msgProps[$this->props['status']] == 2) $body .= _("Complete"); - else if ($msgProps[$this->props['status']] == 3) $body .= _("Wait for other person"); - else if ($msgProps[$this->props['status']] == 4) $body .= _("Deferred"); - } - - if (isset($msgProps[$this->props['percent_complete']])) { - $body .= "\n" . _("Percent Complete") . ":\t". ($msgProps[$this->props['percent_complete']] * 100).'%'; - - if ($msgProps[$this->props['percent_complete']] == 1 && isset($msgProps[$this->props['datecompleted']])) - $body .= "\n" . _("Date Completed") . ":\t". strftime("%A, %B %d, %Y",$msgProps[$this->props['datecompleted']]); - } - $body .= "\n"; - - if (isset($msgProps[$this->props['totalwork']])) $body .= "\n" . _("Total Work") . ":\t". ($msgProps[$this->props['totalwork']]/60) ." " . _("hours"); - if (isset($msgProps[$this->props['actualwork']])) $body .= "\n" . _("Actual Work") . ":\t". ($msgProps[$this->props['actualwork']]/60) ." " . _("hours"); - $body .="\n"; - - if (isset($msgProps[$this->props['owner']])) $body .= "\n" . _("Owner") . ":\t". $msgProps[$this->props['owner']]; - $body .="\n"; - - if (isset($msgProps[$this->props['categories']]) && !empty($msgProps[$this->props['categories']])) $body .= "\nCategories:\t". implode(', ', $msgProps[$this->props['categories']]); - if (isset($msgProps[$this->props['companies']]) && !empty($msgProps[$this->props['companies']])) $body .= "\nCompany:\t". implode(', ', $msgProps[$this->props['companies']]); - if (isset($msgProps[$this->props['billinginformation']])) $body .= "\n" . _("Billing Information") . ":\t". $msgProps[$this->props['billinginformation']]; - if (isset($msgProps[$this->props['mileage']])) $body .= "\n" . _("Mileage") . ":\t". $msgProps[$this->props['mileage']]; - $body .="\n"; - - $content = mapi_message_openproperty($this->message, PR_BODY); - $body .= "\n". trim($content, "\0"); - - return $body; - } - - /** - * Convert from windows-1252 encoded string to UTF-8 string - * - * The same conversion rules as utf8_to_windows1252 apply. - * - * @see Conversion::utf8_to_windows1252() - * - * @param string $string the Windows-1252 string to convert - * @return string UTF-8 representation of the string - */ - function windows1252_to_utf8($string) - { - if (function_exists("iconv")){ - return iconv("Windows-1252", "UTF-8//TRANSLIT", $string); - }else{ - return utf8_encode($string); // no euro support here - } - } - - /** Reclaims ownership of a decline task - * - * Deletes taskrequest properties and recipients from the task message. - */ - function reclaimownership() - { - // Delete task request properties - mapi_deleteprops($this->message, array($this->props['taskglobalobjid'], - $this->props['tasklastuser'], - $this->props['tasklastdelegate'])); - - mapi_setprops($this->message, array($this->props['updatecount'] => 2, - $this->props['taskfcreator'] => true)); - - // Delete recipients - $this->deleteAllRecipients($this->message); - } - - /** Deletes all recipients from given message object - * - *@param $message MAPI message from which recipients are to be removed. - */ - function deleteAllRecipients($message) - { - $recipTable = mapi_message_getrecipienttable($message); - $recipRows = mapi_table_queryallrows($recipTable, array(PR_ROWID)); - - foreach($recipRows as $recipient) - mapi_message_modifyrecipients($message, MODRECIP_REMOVE, array($recipient)); - } - - function sendCompleteUpdate($prefix, $action, $prefixComplete) - { - $messageprops = mapi_getprops($this->message, array($this->props['taskstate'])); - - if(!isset($messageprops[$this->props['taskstate']]) || $messageprops[$this->props['taskstate']] != tdsOWN) - return false; // Can only decline assignee task - - mapi_setprops($this->message, array($this->props['complete'] => true, - $this->props['datecompleted'] => $action["dateCompleted"], - $this->props['status'] => 2, - $this->props['percent_complete'] => 1)); - - $this->doUpdate($prefix, $prefixComplete); - } - } diff --git a/sources/backend/zarafa/mapi/mapi.util.php b/sources/backend/zarafa/mapi/mapi.util.php deleted file mode 100644 index 8bdaeaa..0000000 --- a/sources/backend/zarafa/mapi/mapi.util.php +++ /dev/null @@ -1,338 +0,0 @@ -. - * - */ - - -/** - * Function to make a MAPIGUID from a php string. - * The C++ definition for the GUID is: - * typedef struct _GUID - * { - * unsigned long Data1; - * unsigned short Data2; - * unsigned short Data3; - * unsigned char Data4[8]; - * } GUID; - * - * A GUID is normally represented in the following form: - * {00062008-0000-0000-C000-000000000046} - * - * @param String GUID - */ -function makeGuid($guid) -{ - // remove the { and } from the string and explode it into an array - $guidArray = explode('-', substr($guid, 1,strlen($guid)-2)); - - // convert to hex! - $data1[0] = intval(substr($guidArray[0], 0, 4),16); // we need to split the unsigned long - $data1[1] = intval(substr($guidArray[0], 4, 4),16); - $data2 = intval($guidArray[1], 16); - $data3 = intval($guidArray[2], 16); - - $data4[0] = intval(substr($guidArray[3], 0, 2),16); - $data4[1] = intval(substr($guidArray[3], 2, 2),16); - - for($i=0; $i < 6; $i++) - { - $data4[] = intval(substr($guidArray[4], $i*2, 2),16); - } - - return pack("vvvvCCCCCCCC", $data1[1], $data1[0], $data2, $data3, $data4[0],$data4[1],$data4[2],$data4[3],$data4[4],$data4[5],$data4[6],$data4[7]); -} - -/** - * Function to get a human readable string from a MAPI error code - * - *@param int $errcode the MAPI error code, if not given, we use mapi_last_hresult - *@return string The defined name for the MAPI error code - */ -function get_mapi_error_name($errcode=null) -{ - if ($errcode === null){ - $errcode = mapi_last_hresult(); - } - - if ($errcode !== 0) { - // get_defined_constants(true) is preferred, but crashes PHP - // https://bugs.php.net/bug.php?id=61156 - $allConstants = get_defined_constants(); - - foreach ($allConstants as $key => $value) { - /** - * If PHP encounters a number beyond the bounds of the integer type, - * it will be interpreted as a float instead, so when comparing these error codes - * we have to manually typecast value to integer, so float will be converted in integer, - * but still its out of bound for integer limit so it will be auto adjusted to minus value - */ - if ($errcode == (int) $value) { - // Check that we have an actual MAPI error or warning definition - $prefix = substr($key, 0, 7); - if ($prefix == "MAPI_E_" || $prefix == "MAPI_W_") { - return $key; - } - } - } - } else { - return "NOERROR"; - } - - // error code not found, return hex value (this is a fix for 64-bit systems, we can't use the dechex() function for this) - $result = unpack("H*", pack("N", $errcode)); - return "0x" . $result[1]; -} - -/** - * Parses properties from an array of strings. Each "string" may be either an ULONG, which is a direct property ID, - * or a string with format "PT_TYPE:{GUID}:StringId" or "PT_TYPE:{GUID}:0xXXXX" for named - * properties. - * - * @returns array of properties - */ -function getPropIdsFromStrings($store, $mapping) -{ - $props = array(); - - $ids = array("name"=>array(), "id"=>array(), "guid"=>array(), "type"=>array()); // this array stores all the information needed to retrieve a named property - $num = 0; - - // caching - $guids = array(); - - foreach($mapping as $name=>$val){ - if(is_string($val)) { - $split = explode(":", $val); - - if(count($split) != 3){ // invalid string, ignore - trigger_error(sprintf("Invalid property: %s \"%s\"",$name,$val), E_USER_NOTICE); - continue; - } - - if(substr($split[2], 0, 2) == "0x") { - $id = hexdec(substr($split[2], 2)); - } else { - $id = $split[2]; - } - - // have we used this guid before? - if (!defined($split[1])){ - if (!array_key_exists($split[1], $guids)){ - $guids[$split[1]] = makeguid($split[1]); - } - $guid = $guids[$split[1]]; - }else{ - $guid = constant($split[1]); - } - - // temp store info about named prop, so we have to call mapi_getidsfromnames just one time - $ids["name"][$num] = $name; - $ids["id"][$num] = $id; - $ids["guid"][$num] = $guid; - $ids["type"][$num] = $split[0]; - $num++; - }else{ - // not a named property - $props[$name] = $val; - } - } - - if (empty($ids["id"])){ - return $props; - } - - // get the ids - $named = mapi_getidsfromnames($store, $ids["id"], $ids["guid"]); - foreach($named as $num=>$prop){ - $props[$ids["name"][$num]] = mapi_prop_tag(constant($ids["type"][$num]), mapi_prop_id($prop)); - } - - return $props; -} - -/** - * Check wether a call to mapi_getprops returned errors for some properties. - * mapi_getprops function tries to get values of properties requested but somehow if - * if a property value can not be fetched then it changes type of property tag as PT_ERROR - * and returns error for that particular property, probable errors - * that can be returned as value can be MAPI_E_NOT_FOUND, MAPI_E_NOT_ENOUGH_MEMORY - * - * @param long $property Property to check for error - * @param Array $propArray An array of properties - * @return mixed Gives back false when there is no error, if there is, gives the error - */ -function propIsError($property, $propArray) -{ - if (array_key_exists(mapi_prop_tag(PT_ERROR, mapi_prop_id($property)), $propArray)) { - return $propArray[mapi_prop_tag(PT_ERROR, mapi_prop_id($property))]; - } else { - return false; - } -} - -/******** Macro Functions for PR_DISPLAY_TYPE_EX values *********/ -/** - * check addressbook object is a remote mailuser - */ -function DTE_IS_REMOTE_VALID($value) { - return !!($value & DTE_FLAG_REMOTE_VALID); -} - -/** - * check addressbook object is able to receive permissions - */ -function DTE_IS_ACL_CAPABLE($value) { - return !!($value & DTE_FLAG_ACL_CAPABLE); -} - -function DTE_REMOTE($value) { - return (($value & DTE_MASK_REMOTE) >> 8); -} - -function DTE_LOCAL($value) { - return ($value & DTE_MASK_LOCAL); -} - -/** - * Note: Static function, more like a utility function. - * - * Gets all the items (including recurring items) in the specified calendar in the given timeframe. Items are - * included as a whole if they overlap the interval <$start, $end> (non-inclusive). This means that if the interval - * is <08:00 - 14:00>, the item [6:00 - 8:00> is NOT included, nor is the item [14:00 - 16:00>. However, the item - * [7:00 - 9:00> is included as a whole, and is NOT capped to [8:00 - 9:00>. - * - * @param $store resource The store in which the calendar resides - * @param $calendar resource The calendar to get the items from - * @param $viewstart int Timestamp of beginning of view window - * @param $viewend int Timestamp of end of view window - * @param $propsrequested array Array of properties to return - * @param $rows array Array of rowdata as if they were returned directly from mapi_table_queryrows. Each recurring item is - * expanded so that it seems that there are only many single appointments in the table. - */ -function getCalendarItems($store, $calendar, $viewstart, $viewend, $propsrequested){ - $result = array(); - $properties = getPropIdsFromStrings($store, Array( "duedate" => "PT_SYSTIME:PSETID_Appointment:0x820e", - "startdate" => "PT_SYSTIME:PSETID_Appointment:0x820d", - "enddate_recurring" => "PT_SYSTIME:PSETID_Appointment:0x8236", - "recurring" => "PT_BOOLEAN:PSETID_Appointment:0x8223", - "recurring_data" => "PT_BINARY:PSETID_Appointment:0x8216", - "timezone_data" => "PT_BINARY:PSETID_Appointment:0x8233", - "label" => "PT_LONG:PSETID_Appointment:0x8214" - )); - - // Create a restriction that will discard rows of appointments that are definitely not in our - // requested time frame - - $table = mapi_folder_getcontentstable($calendar); - - $restriction = - // OR - Array(RES_OR, - Array( - Array(RES_AND, // Normal items: itemEnd must be after viewStart, itemStart must be before viewEnd - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_GT, - ULPROPTAG => $properties["duedate"], - VALUE => $viewstart - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_LT, - ULPROPTAG => $properties["startdate"], - VALUE => $viewend - ) - ) - ) - ), - // OR - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => $properties["recurring"], - VALUE => true - ) - ) - ) // EXISTS OR - ); // global OR - - // Get requested properties, plus whatever we need - $proplist = array(PR_ENTRYID, $properties["recurring"], $properties["recurring_data"], $properties["timezone_data"]); - $proplist = array_merge($proplist, $propsrequested); - $propslist = array_unique($proplist); - - $rows = mapi_table_queryallrows($table, $proplist, $restriction); - - // $rows now contains all the items that MAY be in the window; a recurring item needs expansion before including in the output. - - foreach($rows as $row) { - $items = array(); - - if(isset($row[$properties["recurring"]]) && $row[$properties["recurring"]]) { - // Recurring item - $rec = new Recurrence($store, $row); - - // GetItems guarantees that the item overlaps the interval <$viewstart, $viewend> - $occurrences = $rec->getItems($viewstart, $viewend); - foreach($occurrences as $occurrence) { - // The occurrence takes all properties from the main row, but overrides some properties (like start and end obviously) - $item = $occurrence + $row; - array_push($items, $item); - } - - } else { - // Normal item, it matched the search criteria and therefore overlaps the interval <$viewstart, $viewend> - array_push($items, $row); - } - - $result = array_merge($result,$items); - } - - // All items are guaranteed to overlap the interval <$viewstart, $viewend>. Note that we may be returning a few extra - // properties that the caller did not request (recurring, etc). This shouldn't be a problem though. - return $result; -} diff --git a/sources/backend/zarafa/mapi/mapicode.php b/sources/backend/zarafa/mapi/mapicode.php deleted file mode 100644 index b698d13..0000000 --- a/sources/backend/zarafa/mapi/mapicode.php +++ /dev/null @@ -1,246 +0,0 @@ -. - * - */ - - -/** -* Status codes returned by MAPI functions -* -* -*/ - -/* From winerror.h */ -// -// Success codes -// -define('S_OK', 0x00000000); -define('S_FALSE', 0x00000001); -define('SEVERITY_ERROR', 1); - -/* from winerror.h */ - -/** -* Function to make a error -*/ -function make_mapi_e($code) -{ - return (int) mapi_make_scode(1, $code); -} - - -/** -* Function to make an warning -*/ -function make_mapi_s($code) -{ - return (int) mapi_make_scode(0, $code); -} - -/* From mapicode.h */ -/* - * On Windows NT 3.5 and Windows 95, scodes are 32-bit values - * laid out as follows: - * - * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-+-+-+-+-+---------------------+-------------------------------+ - * |S|R|C|N|r| Facility | Code | - * +-+-+-+-+-+---------------------+-------------------------------+ - * - * where - * - * S - Severity - indicates success/fail - * - * 0 - Success - * 1 - Fail (COERROR) - * - * R - reserved portion of the facility code, corresponds to NT's - * second severity bit. - * - * C - reserved portion of the facility code, corresponds to NT's - * C field. - * - * N - reserved portion of the facility code. Used to indicate a - * mapped NT status value. - * - * r - reserved portion of the facility code. Reserved for internal - * use. Used to indicate HRESULT values that are not status - * values, but are instead message ids for display strings. - * - * Facility - is the facility code - * FACILITY_NULL 0x0 - * FACILITY_RPC 0x1 - * FACILITY_DISPATCH 0x2 - * FACILITY_STORAGE 0x3 - * FACILITY_ITF 0x4 - * FACILITY_WIN32 0x7 - * FACILITY_WINDOWS 0x8 - * - * Code - is the facility's status code - * - */ -define('NOERROR' ,0); - -define('MAPI_E_CALL_FAILED' ,(int) 0x80004005); -define('MAPI_E_NOT_ENOUGH_MEMORY' ,(int) 0x8007000E); -define('MAPI_E_INVALID_PARAMETER' ,(int) 0x80070057); -define('MAPI_E_INTERFACE_NOT_SUPPORTED' ,(int) 0x80004002); -define('MAPI_E_NO_ACCESS' ,(int) 0x80070005); - -define('MAPI_E_NO_SUPPORT' ,make_mapi_e(0x102)); -define('MAPI_E_BAD_CHARWIDTH' ,make_mapi_e(0x103)); -define('MAPI_E_STRING_TOO_LONG' ,make_mapi_e(0x105)); -define('MAPI_E_UNKNOWN_FLAGS' ,make_mapi_e(0x106)); -define('MAPI_E_INVALID_ENTRYID' ,make_mapi_e(0x107)); -define('MAPI_E_INVALID_OBJECT' ,make_mapi_e(0x108)); -define('MAPI_E_OBJECT_CHANGED' ,make_mapi_e(0x109)); -define('MAPI_E_OBJECT_DELETED' ,make_mapi_e(0x10A)); -define('MAPI_E_BUSY' ,make_mapi_e(0x10B)); -define('MAPI_E_NOT_ENOUGH_DISK' ,make_mapi_e(0x10D)); -define('MAPI_E_NOT_ENOUGH_RESOURCES' ,make_mapi_e(0x10E)); -define('MAPI_E_NOT_FOUND' ,make_mapi_e(0x10F)); -define('MAPI_E_VERSION' ,make_mapi_e(0x110)); -define('MAPI_E_LOGON_FAILED' ,make_mapi_e(0x111)); -define('MAPI_E_SESSION_LIMIT' ,make_mapi_e(0x112)); -define('MAPI_E_USER_CANCEL' ,make_mapi_e(0x113)); -define('MAPI_E_UNABLE_TO_ABORT' ,make_mapi_e(0x114)); -define('MAPI_E_NETWORK_ERROR' ,make_mapi_e(0x115)); -define('MAPI_E_DISK_ERROR' ,make_mapi_e(0x116)); -define('MAPI_E_TOO_COMPLEX' ,make_mapi_e(0x117)); -define('MAPI_E_BAD_COLUMN' ,make_mapi_e(0x118)); -define('MAPI_E_EXTENDED_ERROR' ,make_mapi_e(0x119)); -define('MAPI_E_COMPUTED' ,make_mapi_e(0x11A)); -define('MAPI_E_CORRUPT_DATA' ,make_mapi_e(0x11B)); -define('MAPI_E_UNCONFIGURED' ,make_mapi_e(0x11C)); -define('MAPI_E_FAILONEPROVIDER' ,make_mapi_e(0x11D)); -define('MAPI_E_UNKNOWN_CPID' ,make_mapi_e(0x11E)); -define('MAPI_E_UNKNOWN_LCID' ,make_mapi_e(0x11F)); - -/* Flavors of E_ACCESSDENIED, used at logon */ - -define('MAPI_E_PASSWORD_CHANGE_REQUIRED' ,make_mapi_e(0x120)); -define('MAPI_E_PASSWORD_EXPIRED' ,make_mapi_e(0x121)); -define('MAPI_E_INVALID_WORKSTATION_ACCOUNT' ,make_mapi_e(0x122)); -define('MAPI_E_INVALID_ACCESS_TIME' ,make_mapi_e(0x123)); -define('MAPI_E_ACCOUNT_DISABLED' ,make_mapi_e(0x124)); - -/* MAPI base function and status object specific errors and warnings */ - -define('MAPI_E_END_OF_SESSION' ,make_mapi_e(0x200)); -define('MAPI_E_UNKNOWN_ENTRYID' ,make_mapi_e(0x201)); -define('MAPI_E_MISSING_REQUIRED_COLUMN' ,make_mapi_e(0x202)); -define('MAPI_W_NO_SERVICE' ,make_mapi_s(0x203)); - -/* Property specific errors and warnings */ - -define('MAPI_E_BAD_VALUE' ,make_mapi_e(0x301)); -define('MAPI_E_INVALID_TYPE' ,make_mapi_e(0x302)); -define('MAPI_E_TYPE_NO_SUPPORT' ,make_mapi_e(0x303)); -define('MAPI_E_UNEXPECTED_TYPE' ,make_mapi_e(0x304)); -define('MAPI_E_TOO_BIG' ,make_mapi_e(0x305)); -define('MAPI_E_DECLINE_COPY' ,make_mapi_e(0x306)); -define('MAPI_E_UNEXPECTED_ID' ,make_mapi_e(0x307)); - -define('MAPI_W_ERRORS_RETURNED' ,make_mapi_s(0x380)); - -/* Table specific errors and warnings */ - -define('MAPI_E_UNABLE_TO_COMPLETE' ,make_mapi_e(0x400)); -define('MAPI_E_TIMEOUT' ,make_mapi_e(0x401)); -define('MAPI_E_TABLE_EMPTY' ,make_mapi_e(0x402)); -define('MAPI_E_TABLE_TOO_BIG' ,make_mapi_e(0x403)); - -define('MAPI_E_INVALID_BOOKMARK' ,make_mapi_e(0x405)); - -define('MAPI_W_POSITION_CHANGED' ,make_mapi_s(0x481)); -define('MAPI_W_APPROX_COUNT' ,make_mapi_s(0x482)); - -/* Transport specific errors and warnings */ - -define('MAPI_E_WAIT' ,make_mapi_e(0x500)); -define('MAPI_E_CANCEL' ,make_mapi_e(0x501)); -define('MAPI_E_NOT_ME' ,make_mapi_e(0x502)); - -define('MAPI_W_CANCEL_MESSAGE' ,make_mapi_s(0x580)); - -/* Message Store, Folder, and Message specific errors and warnings */ - -define('MAPI_E_CORRUPT_STORE' ,make_mapi_e(0x600)); -define('MAPI_E_NOT_IN_QUEUE' ,make_mapi_e(0x601)); -define('MAPI_E_NO_SUPPRESS' ,make_mapi_e(0x602)); -define('MAPI_E_COLLISION' ,make_mapi_e(0x604)); -define('MAPI_E_NOT_INITIALIZED' ,make_mapi_e(0x605)); -define('MAPI_E_NON_STANDARD' ,make_mapi_e(0x606)); -define('MAPI_E_NO_RECIPIENTS' ,make_mapi_e(0x607)); -define('MAPI_E_SUBMITTED' ,make_mapi_e(0x608)); -define('MAPI_E_HAS_FOLDERS' ,make_mapi_e(0x609)); -define('MAPI_E_HAS_MESSAGES' ,make_mapi_e(0x60A)); -define('MAPI_E_FOLDER_CYCLE' ,make_mapi_e(0x60B)); -define('MAPI_E_STORE_FULL' ,make_mapi_e(0x60C)); - -define('MAPI_W_PARTIAL_COMPLETION' ,make_mapi_s(0x680)); - -/* Address Book specific errors and warnings */ - -define('MAPI_E_AMBIGUOUS_RECIP' ,make_mapi_e(0x700)); - -/* ICS errors and warnings */ - -define('SYNC_E_UNKNOWN_FLAGS', MAPI_E_UNKNOWN_FLAGS); -define('SYNC_E_INVALID_PARAMETER', MAPI_E_INVALID_PARAMETER); -define('SYNC_E_ERROR', MAPI_E_CALL_FAILED); -define('SYNC_E_OBJECT_DELETED', make_mapi_e(0x800)); -define('SYNC_E_IGNORE', make_mapi_e(0x801)); -define('SYNC_E_CONFLICT', make_mapi_e(0x802)); -define('SYNC_E_NO_PARENT', make_mapi_e(0x803)); -define('SYNC_E_INCEST', make_mapi_e(0x804)); -define('SYNC_E_UNSYNCHRONIZED', make_mapi_e(0x805)); - -define('SYNC_W_PROGRESS', make_mapi_s(0x820)); -define('SYNC_W_CLIENT_CHANGE_NEWER', make_mapi_s(0x821)); diff --git a/sources/backend/zarafa/mapi/mapidefs.php b/sources/backend/zarafa/mapi/mapidefs.php deleted file mode 100644 index 111375c..0000000 --- a/sources/backend/zarafa/mapi/mapidefs.php +++ /dev/null @@ -1,666 +0,0 @@ -. - * - */ - - -/* Resource types as defined in main.h of the mapi extension */ -define('RESOURCE_SESSION' ,'MAPI Session'); -define('RESOURCE_TABLE' ,'MAPI Table'); -define('RESOURCE_ROWSET' ,'MAPI Rowset'); -define('RESOURCE_MSGSTORE' ,'MAPI Message Store'); -define('RESOURCE_FOLDER' ,'MAPI Folder'); -define('RESOURCE_MESSAGE' ,'MAPI Message'); -define('RESOURCE_ATTACHMENT' ,'MAPI Attachment'); - - -/* Object type */ - -define('MAPI_STORE' ,0x00000001); /* Message Store */ -define('MAPI_ADDRBOOK' ,0x00000002); /* Address Book */ -define('MAPI_FOLDER' ,0x00000003); /* Folder */ -define('MAPI_ABCONT' ,0x00000004); /* Address Book Container */ -define('MAPI_MESSAGE' ,0x00000005); /* Message */ -define('MAPI_MAILUSER' ,0x00000006); /* Individual Recipient */ -define('MAPI_ATTACH' ,0x00000007); /* Attachment */ -define('MAPI_DISTLIST' ,0x00000008); /* Distribution List Recipient */ -define('MAPI_PROFSECT' ,0x00000009); /* Profile Section */ -define('MAPI_STATUS' ,0x0000000A); /* Status Object */ -define('MAPI_SESSION' ,0x0000000B); /* Session */ -define('MAPI_FORMINFO' ,0x0000000C); /* Form Information */ - -define('MV_FLAG' ,0x1000); -define('MV_INSTANCE' ,0x2000); -define('MVI_FLAG' ,MV_FLAG | MV_INSTANCE); - -define('PT_UNSPECIFIED' , 0); /* (Reserved for interface use) type doesn't matter to caller */ -define('PT_NULL' , 1); /* NULL property value */ -define('PT_I2' , 2); /* Signed 16-bit value */ -define('PT_LONG' , 3); /* Signed 32-bit value */ -define('PT_R4' , 4); /* 4-byte floating point */ -define('PT_DOUBLE' , 5); /* Floating point double */ -define('PT_CURRENCY' , 6); /* Signed 64-bit int (decimal w/ 4 digits right of decimal pt) */ -define('PT_APPTIME' , 7); /* Application time */ -define('PT_ERROR' , 10); /* 32-bit error value */ -define('PT_BOOLEAN' , 11); /* 16-bit boolean (non-zero true) */ -define('PT_OBJECT' , 13); /* Embedded object in a property */ -define('PT_I8' , 20); /* 8-byte signed integer */ -define('PT_STRING8' , 30); /* Null terminated 8-bit character string */ -define('PT_UNICODE' , 31); /* Null terminated Unicode string */ -define('PT_SYSTIME' , 64); /* FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */ -define('PT_CLSID' , 72); /* OLE GUID */ -define('PT_BINARY' ,258); /* Uninterpreted (counted byte array) */ -/* Changes are likely to these numbers, and to their structures. */ - -/* Alternate property type names for ease of use */ -define('PT_SHORT' ,PT_I2); -define('PT_I4' ,PT_LONG); -define('PT_FLOAT' ,PT_R4); -define('PT_R8' ,PT_DOUBLE); -define('PT_LONGLONG' ,PT_I8); - - -define('PT_TSTRING' ,PT_STRING8); - - - -define('PT_MV_I2' ,(MV_FLAG | PT_I2)); -define('PT_MV_LONG' ,(MV_FLAG | PT_LONG)); -define('PT_MV_R4' ,(MV_FLAG | PT_R4)); -define('PT_MV_DOUBLE' ,(MV_FLAG | PT_DOUBLE)); -define('PT_MV_CURRENCY' ,(MV_FLAG | PT_CURRENCY)); -define('PT_MV_APPTIME' ,(MV_FLAG | PT_APPTIME)); -define('PT_MV_SYSTIME' ,(MV_FLAG | PT_SYSTIME)); -define('PT_MV_STRING8' ,(MV_FLAG | PT_STRING8)); -define('PT_MV_BINARY' ,(MV_FLAG | PT_BINARY)); -define('PT_MV_UNICODE' ,(MV_FLAG | PT_UNICODE)); -define('PT_MV_CLSID' ,(MV_FLAG | PT_CLSID)); -define('PT_MV_I8' ,(MV_FLAG | PT_I8)); - -define('PT_MV_TSTRING' ,PT_MV_STRING8); -/* bit 0: set if descending, clear if ascending */ - -define('TABLE_SORT_ASCEND' ,(0x00000000)); -define('TABLE_SORT_DESCEND' ,(0x00000001)); -define('TABLE_SORT_COMBINE' ,(0x00000002)); - -define('MAPI_UNICODE' ,0x80000000); - -/* IMAPIFolder Interface --------------------------------------------------- */ -define('CONVENIENT_DEPTH' ,0x00000001); -define('SEARCH_RUNNING' ,0x00000001); -define('SEARCH_REBUILD' ,0x00000002); -define('SEARCH_RECURSIVE' ,0x00000004); -define('SEARCH_FOREGROUND' ,0x00000008); -define('STOP_SEARCH' ,0x00000001); -define('RESTART_SEARCH' ,0x00000002); -define('RECURSIVE_SEARCH' ,0x00000004); -define('SHALLOW_SEARCH' ,0x00000008); -define('FOREGROUND_SEARCH' ,0x00000010); -define('BACKGROUND_SEARCH' ,0x00000020); - -/* IMAPIFolder folder type (enum) */ - -define('FOLDER_ROOT' ,0x00000000); -define('FOLDER_GENERIC' ,0x00000001); -define('FOLDER_SEARCH' ,0x00000002); - -/* CreateMessage */ -/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */ -/****** MAPI_ASSOCIATED ((ULONG) 0x00000040) below */ - -/* CopyMessages */ - -define('MESSAGE_MOVE' ,0x00000001); -define('MESSAGE_DIALOG' ,0x00000002); -/****** MAPI_DECLINE_OK ((ULONG) 0x00000004) above */ - -/* CreateFolder */ - -define('OPEN_IF_EXISTS' ,0x00000001); -/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */ -/****** MAPI_UNICODE ((ULONG) 0x80000000) above */ - -/* DeleteFolder */ - -define('DEL_MESSAGES' ,0x00000001); -define('FOLDER_DIALOG' ,0x00000002); -define('DEL_FOLDERS' ,0x00000004); - -/* EmptyFolder */ -define('DEL_ASSOCIATED' ,0x00000008); - -/* CopyFolder */ - -define('FOLDER_MOVE' ,0x00000001); -/****** FOLDER_DIALOG ((ULONG) 0x00000002) above */ -/****** MAPI_DECLINE_OK ((ULONG) 0x00000004) above */ -define('COPY_SUBFOLDERS' ,0x00000010); -/****** MAPI_UNICODE ((ULONG) 0x80000000) above */ - - -/* SetReadFlags */ - -define('SUPPRESS_RECEIPT' ,0x00000001); -/****** FOLDER_DIALOG ((ULONG) 0x00000002) above */ -define('CLEAR_READ_FLAG' ,0x00000004); -/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */ -define('GENERATE_RECEIPT_ONLY' ,0x00000010); -define('CLEAR_RN_PENDING' ,0x00000020); -define('CLEAR_NRN_PENDING' ,0x00000040); - -/* Flags defined in PR_MESSAGE_FLAGS */ - -define('MSGFLAG_READ' ,0x00000001); -define('MSGFLAG_UNMODIFIED' ,0x00000002); -define('MSGFLAG_SUBMIT' ,0x00000004); -define('MSGFLAG_UNSENT' ,0x00000008); -define('MSGFLAG_HASATTACH' ,0x00000010); -define('MSGFLAG_FROMME' ,0x00000020); -define('MSGFLAG_ASSOCIATED' ,0x00000040); -define('MSGFLAG_RESEND' ,0x00000080); -define('MSGFLAG_RN_PENDING' ,0x00000100); -define('MSGFLAG_NRN_PENDING' ,0x00000200); - -/* GetMessageStatus */ - -define('MSGSTATUS_HIGHLIGHTED' ,0x00000001); -define('MSGSTATUS_TAGGED' ,0x00000002); -define('MSGSTATUS_HIDDEN' ,0x00000004); -define('MSGSTATUS_DELMARKED' ,0x00000008); - -/* Bits for remote message status */ - -define('MSGSTATUS_REMOTE_DOWNLOAD' ,0x00001000); -define('MSGSTATUS_REMOTE_DELETE' ,0x00002000); - -/* SaveContentsSort */ - -define('RECURSIVE_SORT' ,0x00000002); - -/* PR_STATUS property */ - -define('FLDSTATUS_HIGHLIGHTED' ,0x00000001); -define('FLDSTATUS_TAGGED' ,0x00000002); -define('FLDSTATUS_HIDDEN' ,0x00000004); -define('FLDSTATUS_DELMARKED' ,0x00000008); - - -/* IMAPIStatus Interface --------------------------------------------------- */ - -/* Values for PR_RESOURCE_TYPE, _METHODS, _FLAGS */ - -define('MAPI_STORE_PROVIDER' , 33); /* Message Store */ -define('MAPI_AB' , 34); /* Address Book */ -define('MAPI_AB_PROVIDER' , 35); /* Address Book Provider */ -define('MAPI_TRANSPORT_PROVIDER' , 36); /* Transport Provider */ -define('MAPI_SPOOLER' , 37); /* Message Spooler */ -define('MAPI_PROFILE_PROVIDER' , 38); /* Profile Provider */ -define('MAPI_SUBSYSTEM' , 39); /* Overall Subsystem Status */ -define('MAPI_HOOK_PROVIDER' , 40); /* Spooler Hook */ -define('STATUS_VALIDATE_STATE' ,0x00000001); -define('STATUS_SETTINGS_DIALOG' ,0x00000002); -define('STATUS_CHANGE_PASSWORD' ,0x00000004); -define('STATUS_FLUSH_QUEUES' ,0x00000008); - -define('STATUS_DEFAULT_OUTBOUND' ,0x00000001); -define('STATUS_DEFAULT_STORE' ,0x00000002); -define('STATUS_PRIMARY_IDENTITY' ,0x00000004); -define('STATUS_SIMPLE_STORE' ,0x00000008); -define('STATUS_XP_PREFER_LAST' ,0x00000010); -define('STATUS_NO_PRIMARY_IDENTITY' ,0x00000020); -define('STATUS_NO_DEFAULT_STORE' ,0x00000040); -define('STATUS_TEMP_SECTION' ,0x00000080); -define('STATUS_OWN_STORE' ,0x00000100); -define('STATUS_NEED_IPM_TREE' ,0x00000800); -define('STATUS_PRIMARY_STORE' ,0x00001000); -define('STATUS_SECONDARY_STORE' ,0x00002000); - - -/* ------------ */ -/* Random flags */ - -/* Flag for deferred error */ -define('MAPI_DEFERRED_ERRORS' ,0x00000008); - -/* Flag for creating and using Folder Associated Information Messages */ -define('MAPI_ASSOCIATED' ,0x00000040); - -/* Flags for OpenMessageStore() */ - -define('MDB_NO_DIALOG' ,0x00000001); -define('MDB_WRITE' ,0x00000004); -/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) above */ -/****** MAPI_BEST_ACCESS ((ULONG) 0x00000010) above */ -define('MDB_TEMPORARY' ,0x00000020); -define('MDB_NO_MAIL' ,0x00000080); - -/* Flags for OpenAddressBook */ - -define('AB_NO_DIALOG' ,0x00000001); - -/* ((ULONG) 0x00000001 is not a valid flag on ModifyRecipients. */ -define('MODRECIP_ADD' ,0x00000002); -define('MODRECIP_MODIFY' ,0x00000004); -define('MODRECIP_REMOVE' ,0x00000008); - - -define('MAPI_ORIG' ,0); /* Recipient is message originator */ -define('MAPI_TO' ,1); /* Recipient is a primary recipient */ -define('MAPI_CC' ,2); /* Recipient is a copy recipient */ -define('MAPI_BCC' ,3); /* Recipient is blind copy recipient */ - - -/* IAttach Interface ------------------------------------------------------- */ - -/* IAttach attachment methods: PR_ATTACH_METHOD values */ - -define('NO_ATTACHMENT' ,0x00000000); -define('ATTACH_BY_VALUE' ,0x00000001); -define('ATTACH_BY_REFERENCE' ,0x00000002); -define('ATTACH_BY_REF_RESOLVE' ,0x00000003); -define('ATTACH_BY_REF_ONLY' ,0x00000004); -define('ATTACH_EMBEDDED_MSG' ,0x00000005); -define('ATTACH_OLE' ,0x00000006); - -/* OpenProperty - ulFlags */ -define('MAPI_MODIFY' ,0x00000001); -define('MAPI_CREATE' ,0x00000002); -define('STREAM_APPEND' ,0x00000004); -/****** MAPI_DEFERRED_ERRORS ((ULONG) 0x00000008) below */ - - -/* PR_PRIORITY values */ -define('PRIO_URGENT' , 1); -define('PRIO_NORMAL' , 0); -define('PRIO_NONURGENT' ,-1); - -/* PR_SENSITIVITY values */ -define('SENSITIVITY_NONE' ,0x00000000); -define('SENSITIVITY_PERSONAL' ,0x00000001); -define('SENSITIVITY_PRIVATE' ,0x00000002); -define('SENSITIVITY_COMPANY_CONFIDENTIAL' ,0x00000003); - -/* PR_IMPORTANCE values */ -define('IMPORTANCE_LOW' ,0); -define('IMPORTANCE_NORMAL' ,1); -define('IMPORTANCE_HIGH' ,2); - -/* Stream interace values */ -define('STREAM_SEEK_SET' ,0); -define('STREAM_SEEK_CUR' ,1); -define('STREAM_SEEK_END' ,2); - -define('SHOW_SOFT_DELETES' ,0x00000002); -define('DELETE_HARD_DELETE' ,0x00000010); - -/* - * The following flags are used to indicate to the client what access - * level is permissible in the object. They appear in PR_ACCESS in - * message and folder objects as well as in contents and associated - * contents tables - */ -define('MAPI_ACCESS_MODIFY' ,0x00000001); -define('MAPI_ACCESS_READ' ,0x00000002); -define('MAPI_ACCESS_DELETE' ,0x00000004); -define('MAPI_ACCESS_CREATE_HIERARCHY' ,0x00000008); -define('MAPI_ACCESS_CREATE_CONTENTS' ,0x00000010); -define('MAPI_ACCESS_CREATE_ASSOCIATED' ,0x00000020); - -define('MAPI_SEND_NO_RICH_INFO' ,0x00010000); - -/* flags for PR_STORE_SUPPORT_MASK */ -define('STORE_ANSI_OK' ,0x00020000); // The message store supports properties containing ANSI (8-bit) characters. -define('STORE_ATTACH_OK' ,0x00000020); // The message store supports attachments (OLE or non-OLE) to messages. -define('STORE_CATEGORIZE_OK' ,0x00000400); // The message store supports categorized views of tables. -define('STORE_CREATE_OK' ,0x00000010); // The message store supports creation of new messages. -define('STORE_ENTRYID_UNIQUE' ,0x00000001); // Entry identifiers for the objects in the message store are unique, that is, never reused during the life of the store. -define('STORE_HTML_OK' ,0x00010000); // The message store supports Hypertext Markup Language (HTML) messages, stored in the PR_BODY_HTML property. Note that STORE_HTML_OK is not defined in versions of MAPIDEFS.H included with Microsoft� Exchange 2000 Server and earlier. If your development environment uses a MAPIDEFS.H file that does not include STORE_HTML_OK, use the value 0x00010000 instead. -define('STORE_LOCALSTORE' ,0x00080000); // This flag is reserved and should not be used. -define('STORE_MODIFY_OK' ,0x00000008); // The message store supports modification of its existing messages. -define('STORE_MV_PROPS_OK' ,0x00000200); // The message store supports multivalued properties, guarantees the stability of value order in a multivalued property throughout a save operation, and supports instantiation of multivalued properties in tables. -define('STORE_NOTIFY_OK' ,0x00000100); // The message store supports notifications. -define('STORE_OLE_OK' ,0x00000040); // The message store supports OLE attachments. The OLE data is accessible through an IStorage interface, such as that available through the PR_ATTACH_DATA_OBJ property. -define('STORE_PUBLIC_FOLDERS' ,0x00004000); // The folders in this store are public (multi-user), not private (possibly multi-instance but not multi-user). -define('STORE_READONLY' ,0x00000002); // All interfaces for the message store have a read-only access level. -define('STORE_RESTRICTION_OK' ,0x00001000); // The message store supports restrictions. -define('STORE_RTF_OK' ,0x00000800); // The message store supports Rich Text Format (RTF) messages, usually stored compressed, and the store itself keeps PR_BODY and PR_RTF_COMPRESSED synchronized. -define('STORE_SEARCH_OK' ,0x00000004); // The message store supports search-results folders. -define('STORE_SORT_OK' ,0x00002000); // The message store supports sorting views of tables. -define('STORE_SUBMIT_OK' ,0x00000080); // The message store supports marking a message for submission. -define('STORE_UNCOMPRESSED_RTF' ,0x00008000); // The message store supports storage of Rich Text Format (RTF) messages in uncompressed form. An uncompressed RTF stream is identified by the value dwMagicUncompressedRTF in the stream header. The dwMagicUncompressedRTF value is defined in the RTFLIB.H file. -define('STORE_UNICODE_OK' ,0x00040000); // The message store supports properties containing Unicode characters. - - -/* PR_DISPLAY_TYPEs */ -/* For address book contents tables */ -define('DT_MAILUSER' ,0x00000000); -define('DT_DISTLIST' ,0x00000001); -define('DT_FORUM' ,0x00000002); -define('DT_AGENT' ,0x00000003); -define('DT_ORGANIZATION' ,0x00000004); -define('DT_PRIVATE_DISTLIST' ,0x00000005); -define('DT_REMOTE_MAILUSER' ,0x00000006); - -/* For address book hierarchy tables */ -define('DT_MODIFIABLE' ,0x00010000); -define('DT_GLOBAL' ,0x00020000); -define('DT_LOCAL' ,0x00030000); -define('DT_WAN' ,0x00040000); -define('DT_NOT_SPECIFIC' ,0x00050000); - -/* For folder hierarchy tables */ -define('DT_FOLDER' ,0x01000000); -define('DT_FOLDER_LINK' ,0x02000000); -define('DT_FOLDER_SPECIAL' ,0x04000000); - -/* PR_DISPLAY_TYPE_EX values */ -define('DT_ROOM' ,0x00000007); -define('DT_EQUIPMENT' ,0x00000008); -define('DT_SEC_DISTLIST' ,0x00000009); - -/* PR_DISPLAY_TYPE_EX flags */ -define('DTE_FLAG_REMOTE_VALID' ,0x80000000); -define('DTE_FLAG_ACL_CAPABLE' ,0x40000000); /* on for DT_MAILUSER and DT_SEC_DISTLIST */ -define('DTE_MASK_REMOTE' ,0x0000FF00); -define('DTE_MASK_LOCAL' ,0x000000FF); - -/* OlResponseStatus */ -define('olResponseNone' ,0); -define('olResponseOrganized' ,1); -define('olResponseTentative' ,2); -define('olResponseAccepted' ,3); -define('olResponseDeclined' ,4); -define('olResponseNotResponded' ,5); - -/* OlRecipientTrackStatus to set PR_RECIPIENT_TRACKSTATUS in recipient table - * Value of the recipient trackstatus are same as OlResponseStatus but - * recipient trackstatus doesn't have olResponseOrganized and olResponseNotResponded - * and olResponseNone has different interpretation with PR_RECIPIENT_TRACKSTATUS - * so to avoid confusions we have defined new constants. -*/ -define('olRecipientTrackStatusNone' ,0); -define('olRecipientTrackStatusTentative' ,2); -define('olRecipientTrackStatusAccepted' ,3); -define('olRecipientTrackStatusDeclined' ,4); - -/* OlMeetingStatus */ -define('olNonMeeting' ,0); -define('olMeeting' ,1); -define('olMeetingReceived' ,3); -define('olMeetingCanceled' ,5); -define('olMeetingReceivedAndCanceled' ,7); - -/* OlMeetingResponse */ -define('olMeetingTentative' ,2); -define('olMeetingAccepted' ,3); -define('olMeetingDeclined' ,4); - -/* OL Attendee type */ -define('olAttendeeRequired' ,1); -define('olAttendeeOptional' ,2); -define('olAttendeeResource' ,3); - -/* task status */ -define('olTaskNotStarted' ,0); -define('olTaskInProgress' ,1); -define('olTaskComplete' ,2); -define('olTaskWaiting' ,3); -define('olTaskDeferred' ,4); - -/* restrictions */ -define('RES_AND' ,0); -define('RES_OR' ,1); -define('RES_NOT' ,2); -define('RES_CONTENT' ,3); -define('RES_PROPERTY' ,4); -define('RES_COMPAREPROPS' ,5); -define('RES_BITMASK' ,6); -define('RES_SIZE' ,7); -define('RES_EXIST' ,8); -define('RES_SUBRESTRICTION' ,9); -define('RES_COMMENT' ,10); - -/* restriction compares */ -define('RELOP_LT' ,0); -define('RELOP_LE' ,1); -define('RELOP_GT' ,2); -define('RELOP_GE' ,3); -define('RELOP_EQ' ,4); -define('RELOP_NE' ,5); -define('RELOP_RE' ,6); - -/* string 'fuzzylevel' */ -define('FL_FULLSTRING' ,0x00000000); -define('FL_SUBSTRING' ,0x00000001); -define('FL_PREFIX' ,0x00000002); -define('FL_IGNORECASE' ,0x00010000); -define('FL_IGNORENONSPACE' ,0x00020000); -define('FL_LOOSE' ,0x00040000); - -/* bitmask restriction types */ -define('BMR_EQZ' ,0x00000000); -define('BMR_NEZ' ,0x00000001); - -/* array index values of restrictions -- same values are used in php-ext/main.cpp::PHPArraytoSRestriction() */ -define('VALUE' ,0); // propval -define('RELOP' ,1); // compare method -define('FUZZYLEVEL' ,2); // string search flags -define('CB' ,3); // size restriction -define('ULTYPE' ,4); // bit mask restriction type BMR_xxx -define('ULMASK' ,5); // bitmask -define('ULPROPTAG' ,6); // property -define('ULPROPTAG1' ,7); // RES_COMPAREPROPS 1st property -define('ULPROPTAG2' ,8); // RES_COMPAREPROPS 2nd property -define('PROPS' ,9); // RES_COMMENT properties -define('RESTRICTION' ,10); // RES_COMMENT and RES_SUBRESTRICTION restriction - -/* GUID's for PR_MDB_PROVIDER */ -define("ZARAFA_SERVICE_GUID" ,makeGuid("{3C253DCA-D227-443C-94FE-425FAB958C19}")); // default store -define("ZARAFA_STORE_PUBLIC_GUID" ,makeGuid("{D47F4A09-D3BD-493C-B2FC-3C90BBCB48D4}")); // public store -define("ZARAFA_STORE_DELEGATE_GUID" ,makeGuid("{7C7C1085-BC6D-4E53-9DAB-8A53F8DEF808}")); // other store -define('ZARAFA_STORE_ARCHIVER_GUID' ,makeGuid("{BC8953AD-2E3F-4172-9404-896FF459870F}")); // archive store - -/* global profile section guid */ -define('pbGlobalProfileSectionGuid' ,makeGuid("{C8B0DB13-05AA-1A10-9BB0-00AA002FC45A}")); - -/* Zarafa Contacts provider GUID */ -define('ZARAFA_CONTACTS_GUID' ,makeGuid("{30047F72-92E3-DA4F-B86A-E52A7FE46571}")); - -/* Permissions */ - -// Get permission type -define('ACCESS_TYPE_DENIED' ,1); -define('ACCESS_TYPE_GRANT' ,2); -define('ACCESS_TYPE_BOTH' ,3); - -define('ecRightsNone' ,0x00000000); -define('ecRightsReadAny' ,0x00000001); -define('ecRightsCreate' ,0x00000002); -define('ecRightsEditOwned' ,0x00000008); -define('ecRightsDeleteOwned' ,0x00000010); -define('ecRightsEditAny' ,0x00000020); -define('ecRightsDeleteAny' ,0x00000040); -define('ecRightsCreateSubfolder' ,0x00000080); -define('ecRightsFolderAccess' ,0x00000100); -//define('ecrightsContact' ,0x00000200); -define('ecRightsFolderVisible' ,0x00000400); - -define('ecRightsAll' ,ecRightsReadAny | ecRightsCreate | ecRightsEditOwned | ecRightsDeleteOwned | ecRightsEditAny | ecRightsDeleteAny | ecRightsCreateSubfolder | ecRightsFolderAccess | ecRightsFolderVisible); -define('ecRightsFullControl' ,ecRightsReadAny | ecRightsCreate | ecRightsEditOwned | ecRightsDeleteOwned | ecRightsEditAny | ecRightsDeleteAny | ecRightsCreateSubfolder | ecRightsFolderVisible); -define('ecRightsDefault' ,ecRightsNone | ecRightsFolderVisible); -define('ecRightsDefaultPublic' ,ecRightsReadAny | ecRightsFolderVisible); -define('ecRightsAdmin' ,0x00001000); -define('ecRightsAllMask' ,0x000015FB); - -// Right change indication -define('RIGHT_NORMAL' ,0x00); -define('RIGHT_NEW' ,0x01); -define('RIGHT_MODIFY' ,0x02); -define('RIGHT_DELETED' ,0x04); -define('RIGHT_AUTOUPDATE_DENIED' ,0x08); - -// IExchangeModifyTable: defines for rules -define('ROWLIST_REPLACE' ,0x0001); -define('ROW_ADD' ,0x0001); -define('ROW_MODIFY' ,0x0002); -define('ROW_REMOVE' ,0x0004); -define('ROW_EMPTY' ,(ROW_ADD|ROW_REMOVE)); - -// new property types -define('PT_SRESTRICTION' ,0x00FD); -define('PT_ACTIONS' ,0x00FE); -// unused, I believe -define('PT_FILE_HANDLE' ,0x0103); -define('PT_FILE_EA' ,0x0104); -define('PT_VIRTUAL' ,0x0105); - -// rules state -define('ST_DISABLED' ,0x0000); -define('ST_ENABLED' ,0x0001); -define('ST_ERROR' ,0x0002); -define('ST_ONLY_WHEN_OOF' ,0x0004); -define('ST_KEEP_OOF_HIST' ,0x0008); -define('ST_EXIT_LEVEL' ,0x0010); -define('ST_SKIP_IF_SCL_IS_SAFE' ,0x0020); -define('ST_RULE_PARSE_ERROR' ,0x0040); -define('ST_CLEAR_OOF_HIST' ,0x80000000); - -// action types -define('OP_MOVE' ,1); -define('OP_COPY' ,2); -define('OP_REPLY' ,3); -define('OP_OOF_REPLY' ,4); -define('OP_DEFER_ACTION' ,5); -define('OP_BOUNCE' ,6); -define('OP_FORWARD' ,7); -define('OP_DELEGATE' ,8); -define('OP_TAG' ,9); -define('OP_DELETE' ,10); -define('OP_MARK_AS_READ' ,11); - -// for OP_REPLY -define('DO_NOT_SEND_TO_ORIGINATOR' ,1); -define('STOCK_REPLY_TEMPLATE' ,2); - -// for OP_FORWARD -define('FWD_PRESERVE_SENDER' ,1); -define('FWD_DO_NOT_MUNGE_MSG' ,2); -define('FWD_AS_ATTACHMENT' ,4); - -// scBounceCodevalues -define('BOUNCE_MESSAGE_SIZE_TOO_LARGE' ,13); -define('BOUNCE_FORMS_MISMATCH' ,31); -define('BOUNCE_ACCESS_DENIED' ,38); - -// Free/busystatus -define('fbFree' ,0); -define('fbTentative' ,1); -define('fbBusy' ,2); -define('fbOutOfOffice' ,3); -define('fbWorkingElsewhere' ,4); - -/* ICS flags */ - -// For Synchronize() -define('SYNC_UNICODE' ,0x01); -define('SYNC_NO_DELETIONS' ,0x02); -define('SYNC_NO_SOFT_DELETIONS' ,0x04); -define('SYNC_READ_STATE' ,0x08); -define('SYNC_ASSOCIATED' ,0x10); -define('SYNC_NORMAL' ,0x20); -define('SYNC_NO_CONFLICTS' ,0x40); -define('SYNC_ONLY_SPECIFIED_PROPS' ,0x80); -define('SYNC_NO_FOREIGN_KEYS' ,0x100); -define('SYNC_LIMITED_IMESSAGE' ,0x200); -define('SYNC_CATCHUP' ,0x400); -define('SYNC_NEW_MESSAGE' ,0x800); // only applicable to ImportMessageChange() -define('SYNC_MSG_SELECTIVE' ,0x1000); // Used internally. Will reject if used by clients. -define('SYNC_BEST_BODY' ,0x2000); -define('SYNC_IGNORE_SPECIFIED_ON_ASSOCIATED' ,0x4000); -define('SYNC_PROGRESS_MODE' ,0x8000); // AirMapi progress mode -define('SYNC_FXRECOVERMODE' ,0x10000); -define('SYNC_DEFER_CONFIG' ,0x20000); -define('SYNC_FORCE_UNICODE' ,0x40000); // Forces server to return Unicode properties - -define('EMS_AB_ADDRESS_LOOKUP' ,0x00000001); // Flag for resolvename to resolve only exact matches - -define('TBL_BATCH' ,0x00000002); // Batch multiple table commands - -/* Flags for recipients in exceptions */ -define('recipSendable' ,0x00000001); // sendable attendee. -define('recipOrganizer' ,0x00000002); // meeting organizer -define('recipExceptionalResponse' ,0x00000010); // attendee gave a response for the exception -define('recipExceptionalDeleted' ,0x00000020); // recipientRow exists, but it is treated as if the corresponding recipient is deleted from meeting -define('recipOriginal' ,0x00000100); // recipient is an original Attendee -define('recipReserved' ,0x00000200); - -/* Flags which indicates type of Meeting Object */ -define('mtgEmpty' ,0x00000000); // Unspecified. -define('mtgRequest' ,0x00000001); // Initial meeting request. -define('mtgFull' ,0x00010000); // Full update. -define('mtgInfo' ,0x00020000); // Informational update. -define('mtgOutOfDate' ,0x00080000); // A newer Meeting Request object or Meeting Update object was received after this one. -define('mtgDelegatorCopy' ,0x00100000); // This is set on the delegator's copy when a delegate will handle meeting-related objects. - -define('MAPI_ONE_OFF_UNICODE' ,0x8000); // the flag that defines whether the embedded strings are Unicode in one off entryids. -define('MAPI_ONE_OFF_NO_RICH_INFO' ,0x0001); // the flag that specifies whether the recipient gets TNEF or not. - -/* Mask flags for mapi_msgstore_advise */ -define('fnevCriticalError' ,0x00000001); -define('fnevNewMail' ,0x00000002); -define('fnevObjectCreated' ,0x00000004); -define('fnevObjectDeleted' ,0x00000008); -define('fnevObjectModified' ,0x00000010); -define('fnevObjectMoved' ,0x00000020); -define('fnevObjectCopied' ,0x00000040); -define('fnevSearchComplete' ,0x00000080); -define('fnevTableModified' ,0x00000100); -define('fnevStatusObjectModified' ,0x00000200); -define('fnevReservedForMapi' ,0x40000000); -define('fnevExtended' ,0x80000000); diff --git a/sources/backend/zarafa/mapi/mapiguid.php b/sources/backend/zarafa/mapi/mapiguid.php deleted file mode 100644 index 7ece46a..0000000 --- a/sources/backend/zarafa/mapi/mapiguid.php +++ /dev/null @@ -1,72 +0,0 @@ -. - * - */ - -define('IID_IStream', makeguid("{0000000c-0000-0000-c000-000000000046}")); -define('IID_IMAPITable', makeguid("{00020301-0000-0000-c000-000000000046}")); -define('IID_IMessage', makeguid("{00020307-0000-0000-c000-000000000046}")); -define('IID_IExchangeExportChanges', makeguid("{a3ea9cc0-d1b2-11cd-80fc-00aa004bba0b}")); -define('IID_IExchangeImportContentsChanges', makeguid("{f75abfa0-d0e0-11cd-80fc-00aa004bba0b}")); -define('IID_IExchangeImportHierarchyChanges', makeguid("{85a66cf0-d0e0-11cd-80fc-00aa004bba0b}")); - -define('PSETID_Appointment', makeguid("{00062002-0000-0000-C000-000000000046}")); -define('PSETID_Task', makeguid("{00062003-0000-0000-C000-000000000046}")); -define('PSETID_Address', makeguid("{00062004-0000-0000-C000-000000000046}")); -define('PSETID_Common', makeguid("{00062008-0000-0000-C000-000000000046}")); -define('PSETID_Log', makeguid("{0006200A-0000-0000-C000-000000000046}")); -define('PSETID_Note', makeguid("{0006200E-0000-0000-C000-000000000046}")); -define('PSETID_Meeting', makeguid("{6ED8DA90-450B-101B-98DA-00AA003F1305}")); -define('PSETID_Archive', makeguid("{72E98EBC-57D2-4AB5-B0AA-D50A7B531CB9}")); - -define('PS_MAPI', makeguid("{00020328-0000-0000-C000-000000000046}")); -define('PS_PUBLIC_STRINGS', makeguid("{00020329-0000-0000-C000-000000000046}")); -define('PS_INTERNET_HEADERS', makeguid("{00020386-0000-0000-c000-000000000046}")); - -// sk added for Z-Push -define ('PSETID_AirSync', makeguid("{71035549-0739-4DCB-9163-00F0580DBBDF}")); diff --git a/sources/backend/zarafa/mapi/mapitags.php b/sources/backend/zarafa/mapi/mapitags.php deleted file mode 100644 index 7264c34..0000000 --- a/sources/backend/zarafa/mapi/mapitags.php +++ /dev/null @@ -1,1254 +0,0 @@ -. - * - */ - -if (!function_exists("mapi_prop_tag")) - throw new FatalMisconfigurationException("PHP-MAPI extension is not available"); - -define('PR_ACKNOWLEDGEMENT_MODE' ,mapi_prop_tag(PT_LONG, 0x0001)); -define('PR_ALTERNATE_RECIPIENT_ALLOWED' ,mapi_prop_tag(PT_BOOLEAN, 0x0002)); -define('PR_AUTHORIZING_USERS' ,mapi_prop_tag(PT_BINARY, 0x0003)); -define('PR_AUTO_FORWARD_COMMENT' ,mapi_prop_tag(PT_TSTRING, 0x0004)); -define('PR_AUTO_FORWARD_COMMENT_W' ,mapi_prop_tag(PT_UNICODE, 0x0004)); -define('PR_AUTO_FORWARD_COMMENT_A' ,mapi_prop_tag(PT_STRING8, 0x0004)); -define('PR_AUTO_FORWARDED' ,mapi_prop_tag(PT_BOOLEAN, 0x0005)); -define('PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID' ,mapi_prop_tag(PT_BINARY, 0x0006)); -define('PR_CONTENT_CORRELATOR' ,mapi_prop_tag(PT_BINARY, 0x0007)); -define('PR_CONTENT_IDENTIFIER' ,mapi_prop_tag(PT_TSTRING, 0x0008)); -define('PR_CONTENT_IDENTIFIER_W' ,mapi_prop_tag(PT_UNICODE, 0x0008)); -define('PR_CONTENT_IDENTIFIER_A' ,mapi_prop_tag(PT_STRING8, 0x0008)); -define('PR_CONTENT_LENGTH' ,mapi_prop_tag(PT_LONG, 0x0009)); -define('PR_CONTENT_RETURN_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x000A)); - - - -define('PR_CONVERSATION_KEY' ,mapi_prop_tag(PT_BINARY, 0x000B)); - -define('PR_CONVERSION_EITS' ,mapi_prop_tag(PT_BINARY, 0x000C)); -define('PR_CONVERSION_WITH_LOSS_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x000D)); -define('PR_CONVERTED_EITS' ,mapi_prop_tag(PT_BINARY, 0x000E)); -define('PR_DEFERRED_DELIVERY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x000F)); -define('PR_DELIVER_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0010)); -define('PR_DISCARD_REASON' ,mapi_prop_tag(PT_LONG, 0x0011)); -define('PR_DISCLOSURE_OF_RECIPIENTS' ,mapi_prop_tag(PT_BOOLEAN, 0x0012)); -define('PR_DL_EXPANSION_HISTORY' ,mapi_prop_tag(PT_BINARY, 0x0013)); -define('PR_DL_EXPANSION_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x0014)); -define('PR_EXPIRY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0015)); -define('PR_IMPLICIT_CONVERSION_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x0016)); -define('PR_IMPORTANCE' ,mapi_prop_tag(PT_LONG, 0x0017)); -define('PR_IPM_ID' ,mapi_prop_tag(PT_BINARY, 0x0018)); -define('PR_LATEST_DELIVERY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0019)); -define('PR_MESSAGE_CLASS' ,mapi_prop_tag(PT_TSTRING, 0x001A)); -define('PR_MESSAGE_CLASS_W' ,mapi_prop_tag(PT_UNICODE, 0x001A)); -define('PR_MESSAGE_CLASS_A' ,mapi_prop_tag(PT_STRING8, 0x001A)); -define('PR_MESSAGE_DELIVERY_ID' ,mapi_prop_tag(PT_BINARY, 0x001B)); - - - - - -define('PR_MESSAGE_SECURITY_LABEL' ,mapi_prop_tag(PT_BINARY, 0x001E)); -define('PR_OBSOLETED_IPMS' ,mapi_prop_tag(PT_BINARY, 0x001F)); -define('PR_ORIGINALLY_INTENDED_RECIPIENT_NAME' ,mapi_prop_tag(PT_BINARY, 0x0020)); -define('PR_ORIGINAL_EITS' ,mapi_prop_tag(PT_BINARY, 0x0021)); -define('PR_ORIGINATOR_CERTIFICATE' ,mapi_prop_tag(PT_BINARY, 0x0022)); -define('PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0023)); -define('PR_ORIGINATOR_RETURN_ADDRESS' ,mapi_prop_tag(PT_BINARY, 0x0024)); - -define('PR_PARENT_KEY' ,mapi_prop_tag(PT_BINARY, 0x0025)); -define('PR_PRIORITY' ,mapi_prop_tag(PT_LONG, 0x0026)); - -define('PR_ORIGIN_CHECK' ,mapi_prop_tag(PT_BINARY, 0x0027)); -define('PR_PROOF_OF_SUBMISSION_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0028)); -define('PR_READ_RECEIPT_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0029)); -define('PR_RECEIPT_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x002A)); -define('PR_RECIPIENT_REASSIGNMENT_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x002B)); -define('PR_REDIRECTION_HISTORY' ,mapi_prop_tag(PT_BINARY, 0x002C)); -define('PR_RELATED_IPMS' ,mapi_prop_tag(PT_BINARY, 0x002D)); -define('PR_ORIGINAL_SENSITIVITY' ,mapi_prop_tag(PT_LONG, 0x002E)); -define('PR_LANGUAGES' ,mapi_prop_tag(PT_TSTRING, 0x002F)); -define('PR_LANGUAGES_W' ,mapi_prop_tag(PT_UNICODE, 0x002F)); -define('PR_LANGUAGES_A' ,mapi_prop_tag(PT_STRING8, 0x002F)); -define('PR_REPLY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0030)); -define('PR_REPORT_TAG' ,mapi_prop_tag(PT_BINARY, 0x0031)); -define('PR_REPORT_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0032)); -define('PR_RETURNED_IPM' ,mapi_prop_tag(PT_BOOLEAN, 0x0033)); -define('PR_SECURITY' ,mapi_prop_tag(PT_LONG, 0x0034)); -define('PR_INCOMPLETE_COPY' ,mapi_prop_tag(PT_BOOLEAN, 0x0035)); -define('PR_SENSITIVITY' ,mapi_prop_tag(PT_LONG, 0x0036)); -define('PR_SUBJECT' ,mapi_prop_tag(PT_TSTRING, 0x0037)); -define('PR_SUBJECT_W' ,mapi_prop_tag(PT_UNICODE, 0x0037)); -define('PR_SUBJECT_A' ,mapi_prop_tag(PT_STRING8, 0x0037)); -define('PR_SUBJECT_IPM' ,mapi_prop_tag(PT_BINARY, 0x0038)); -define('PR_CLIENT_SUBMIT_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0039)); -define('PR_REPORT_NAME' ,mapi_prop_tag(PT_TSTRING, 0x003A)); -define('PR_REPORT_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x003A)); -define('PR_REPORT_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x003A)); -define('PR_SENT_REPRESENTING_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x003B)); -define('PR_X400_CONTENT_TYPE' ,mapi_prop_tag(PT_BINARY, 0x003C)); -define('PR_SUBJECT_PREFIX' ,mapi_prop_tag(PT_TSTRING, 0x003D)); -define('PR_SUBJECT_PREFIX_W' ,mapi_prop_tag(PT_UNICODE, 0x003D)); -define('PR_SUBJECT_PREFIX_A' ,mapi_prop_tag(PT_STRING8, 0x003D)); -define('PR_NON_RECEIPT_REASON' ,mapi_prop_tag(PT_LONG, 0x003E)); -define('PR_RECEIVED_BY_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x003F)); -define('PR_RECEIVED_BY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x0040)); -define('PR_RECEIVED_BY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x0040)); -define('PR_RECEIVED_BY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x0040)); -define('PR_SENT_REPRESENTING_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0041)); -define('PR_SENT_REPRESENTING_NAME' ,mapi_prop_tag(PT_TSTRING, 0x0042)); -define('PR_SENT_REPRESENTING_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x0042)); -define('PR_SENT_REPRESENTING_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x0042)); -define('PR_RCVD_REPRESENTING_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0043)); -define('PR_RCVD_REPRESENTING_NAME' ,mapi_prop_tag(PT_TSTRING, 0x0044)); -define('PR_RCVD_REPRESENTING_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x0044)); -define('PR_RCVD_REPRESENTING_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x0044)); -define('PR_REPORT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0045)); -define('PR_READ_RECEIPT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0046)); -define('PR_MESSAGE_SUBMISSION_ID' ,mapi_prop_tag(PT_BINARY, 0x0047)); -define('PR_PROVIDER_SUBMIT_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0048)); -define('PR_ORIGINAL_SUBJECT' ,mapi_prop_tag(PT_TSTRING, 0x0049)); -define('PR_ORIGINAL_SUBJECT_W' ,mapi_prop_tag(PT_UNICODE, 0x0049)); -define('PR_ORIGINAL_SUBJECT_A' ,mapi_prop_tag(PT_STRING8, 0x0049)); -define('PR_DISC_VAL' ,mapi_prop_tag(PT_BOOLEAN, 0x004A)); -define('PR_ORIG_MESSAGE_CLASS' ,mapi_prop_tag(PT_TSTRING, 0x004B)); -define('PR_ORIG_MESSAGE_CLASS_W' ,mapi_prop_tag(PT_UNICODE, 0x004B)); -define('PR_ORIG_MESSAGE_CLASS_A' ,mapi_prop_tag(PT_STRING8, 0x004B)); -define('PR_ORIGINAL_AUTHOR_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x004C)); -define('PR_ORIGINAL_AUTHOR_NAME' ,mapi_prop_tag(PT_TSTRING, 0x004D)); -define('PR_ORIGINAL_AUTHOR_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x004D)); -define('PR_ORIGINAL_AUTHOR_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x004D)); -define('PR_ORIGINAL_SUBMIT_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x004E)); -define('PR_REPLY_RECIPIENT_ENTRIES' ,mapi_prop_tag(PT_BINARY, 0x004F)); -define('PR_REPLY_RECIPIENT_NAMES' ,mapi_prop_tag(PT_TSTRING, 0x0050)); -define('PR_REPLY_RECIPIENT_NAMES_W' ,mapi_prop_tag(PT_UNICODE, 0x0050)); -define('PR_REPLY_RECIPIENT_NAMES_A' ,mapi_prop_tag(PT_STRING8, 0x0050)); - -define('PR_RECEIVED_BY_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0051)); -define('PR_RCVD_REPRESENTING_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0052)); -define('PR_READ_RECEIPT_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0053)); -define('PR_REPORT_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0054)); -define('PR_ORIGINAL_DELIVERY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0055)); -define('PR_ORIGINAL_AUTHOR_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0056)); - -define('PR_MESSAGE_TO_ME' ,mapi_prop_tag(PT_BOOLEAN, 0x0057)); -define('PR_MESSAGE_CC_ME' ,mapi_prop_tag(PT_BOOLEAN, 0x0058)); -define('PR_MESSAGE_RECIP_ME' ,mapi_prop_tag(PT_BOOLEAN, 0x0059)); - -define('PR_ORIGINAL_SENDER_NAME' ,mapi_prop_tag(PT_TSTRING, 0x005A)); -define('PR_ORIGINAL_SENDER_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x005A)); -define('PR_ORIGINAL_SENDER_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x005A)); -define('PR_ORIGINAL_SENDER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x005B)); -define('PR_ORIGINAL_SENDER_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x005C)); -define('PR_ORIGINAL_SENT_REPRESENTING_NAME' ,mapi_prop_tag(PT_TSTRING, 0x005D)); -define('PR_ORIGINAL_SENT_REPRESENTING_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x005D)); -define('PR_ORIGINAL_SENT_REPRESENTING_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x005D)); -define('PR_ORIGINAL_SENT_REPRESENTING_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x005E)); -define('PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x005F)); - -define('PR_START_DATE' ,mapi_prop_tag(PT_SYSTIME, 0x0060)); -define('PR_END_DATE' ,mapi_prop_tag(PT_SYSTIME, 0x0061)); -define('PR_OWNER_APPT_ID' ,mapi_prop_tag(PT_LONG, 0x0062)); -define('PR_RESPONSE_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0063)); - -define('PR_SENT_REPRESENTING_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0064)); -define('PR_SENT_REPRESENTING_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0064)); -define('PR_SENT_REPRESENTING_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0064)); -define('PR_SENT_REPRESENTING_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0065)); -define('PR_SENT_REPRESENTING_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x0065)); -define('PR_SENT_REPRESENTING_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x0065)); - -define('PR_ORIGINAL_SENDER_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0066)); -define('PR_ORIGINAL_SENDER_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0066)); -define('PR_ORIGINAL_SENDER_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0066)); -define('PR_ORIGINAL_SENDER_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0067)); -define('PR_ORIGINAL_SENDER_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x0067)); -define('PR_ORIGINAL_SENDER_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x0067)); - -define('PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0068)); -define('PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0068)); -define('PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0068)); -define('PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0069)); -define('PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_W',mapi_prop_tag(PT_UNICODE, 0x0069)); -define('PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_A',mapi_prop_tag(PT_STRING8, 0x0069)); - -define('PR_CONVERSATION_TOPIC' ,mapi_prop_tag(PT_TSTRING, 0x0070)); -define('PR_CONVERSATION_TOPIC_W' ,mapi_prop_tag(PT_UNICODE, 0x0070)); -define('PR_CONVERSATION_TOPIC_A' ,mapi_prop_tag(PT_STRING8, 0x0070)); -define('PR_CONVERSATION_INDEX' ,mapi_prop_tag(PT_BINARY, 0x0071)); - -define('PR_ORIGINAL_DISPLAY_BCC' ,mapi_prop_tag(PT_TSTRING, 0x0072)); -define('PR_ORIGINAL_DISPLAY_BCC_W' ,mapi_prop_tag(PT_UNICODE, 0x0072)); -define('PR_ORIGINAL_DISPLAY_BCC_A' ,mapi_prop_tag(PT_STRING8, 0x0072)); -define('PR_ORIGINAL_DISPLAY_CC' ,mapi_prop_tag(PT_TSTRING, 0x0073)); -define('PR_ORIGINAL_DISPLAY_CC_W' ,mapi_prop_tag(PT_UNICODE, 0x0073)); -define('PR_ORIGINAL_DISPLAY_CC_A' ,mapi_prop_tag(PT_STRING8, 0x0073)); -define('PR_ORIGINAL_DISPLAY_TO' ,mapi_prop_tag(PT_TSTRING, 0x0074)); -define('PR_ORIGINAL_DISPLAY_TO_W' ,mapi_prop_tag(PT_UNICODE, 0x0074)); -define('PR_ORIGINAL_DISPLAY_TO_A' ,mapi_prop_tag(PT_STRING8, 0x0074)); - -define('PR_RECEIVED_BY_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0075)); -define('PR_RECEIVED_BY_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0075)); -define('PR_RECEIVED_BY_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0075)); -define('PR_RECEIVED_BY_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0076)); -define('PR_RECEIVED_BY_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x0076)); -define('PR_RECEIVED_BY_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x0076)); - -define('PR_RCVD_REPRESENTING_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0077)); -define('PR_RCVD_REPRESENTING_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0077)); -define('PR_RCVD_REPRESENTING_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0077)); -define('PR_RCVD_REPRESENTING_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0078)); -define('PR_RCVD_REPRESENTING_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x0078)); -define('PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x0078)); - -define('PR_ORIGINAL_AUTHOR_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0079)); -define('PR_ORIGINAL_AUTHOR_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0079)); -define('PR_ORIGINAL_AUTHOR_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0079)); -define('PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x007A)); -define('PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x007A)); -define('PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x007A)); - -define('PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x007B)); -define('PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x007B)); -define('PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x007B)); -define('PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x007C)); -define('PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x007C)); -define('PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x007C)); - -define('PR_TRANSPORT_MESSAGE_HEADERS' ,mapi_prop_tag(PT_TSTRING, 0x007D)); -define('PR_TRANSPORT_MESSAGE_HEADERS_W' ,mapi_prop_tag(PT_UNICODE, 0x007D)); -define('PR_TRANSPORT_MESSAGE_HEADERS_A' ,mapi_prop_tag(PT_STRING8, 0x007D)); - -define('PR_DELEGATION' ,mapi_prop_tag(PT_BINARY, 0x007E)); - -define('PR_TNEF_CORRELATION_KEY' ,mapi_prop_tag(PT_BINARY, 0x007F)); - -define('PR_MDN_DISPOSITION_TYPE' ,mapi_prop_tag(PT_STRING8, 0x0080)); -define('PR_MDN_DISPOSITION_SENDINGMODE' ,mapi_prop_tag(PT_STRING8, 0x0081)); - -define('PR_USER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x6618+0x01)); -define('PR_USER_NAME' ,mapi_prop_tag(PT_STRING8, 0x6618+0x02)); -define('PR_MAILBOX_OWNER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x6618+0x03)); -define('PR_MAILBOX_OWNER_NAME' ,mapi_prop_tag(PT_STRING8, 0x6618+0x04)); - -define('PR_HIERARCHY_SYNCHRONIZER' ,mapi_prop_tag(PT_OBJECT, 0x6618+0x14)); -define('PR_CONTENTS_SYNCHRONIZER' ,mapi_prop_tag(PT_OBJECT, 0x6618+0x15)); -define('PR_COLLECTOR' ,mapi_prop_tag(PT_OBJECT, 0x6618+0x16)); - -define('PR_SMTP_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x39FE)); - - -/* - * Message content properties - */ - -define('PR_BODY' ,mapi_prop_tag(PT_TSTRING, 0x1000)); -define('PR_HTML' ,mapi_prop_tag(PT_BINARY, 0x1013)); -define('PR_BODY_W' ,mapi_prop_tag(PT_UNICODE, 0x1000)); -define('PR_BODY_A' ,mapi_prop_tag(PT_STRING8, 0x1000)); -define('PR_REPORT_TEXT' ,mapi_prop_tag(PT_TSTRING, 0x1001)); -define('PR_REPORT_TEXT_W' ,mapi_prop_tag(PT_UNICODE, 0x1001)); -define('PR_REPORT_TEXT_A' ,mapi_prop_tag(PT_STRING8, 0x1001)); -define('PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY' ,mapi_prop_tag(PT_BINARY, 0x1002)); -define('PR_REPORTING_DL_NAME' ,mapi_prop_tag(PT_BINARY, 0x1003)); -define('PR_REPORTING_MTA_CERTIFICATE' ,mapi_prop_tag(PT_BINARY, 0x1004)); - -/* Removed 'PR_REPORT_ORIGIN_AUTHENTICATION_CHECK with DCR 3865, use 'PR_ORIGIN_CHECK */ - -define('PR_RTF_SYNC_BODY_CRC' ,mapi_prop_tag(PT_LONG, 0x1006)); -define('PR_RTF_SYNC_BODY_COUNT' ,mapi_prop_tag(PT_LONG, 0x1007)); -define('PR_RTF_SYNC_BODY_TAG' ,mapi_prop_tag(PT_TSTRING, 0x1008)); -define('PR_RTF_SYNC_BODY_TAG_W' ,mapi_prop_tag(PT_UNICODE, 0x1008)); -define('PR_RTF_SYNC_BODY_TAG_A' ,mapi_prop_tag(PT_STRING8, 0x1008)); -define('PR_RTF_COMPRESSED' ,mapi_prop_tag(PT_BINARY, 0x1009)); -define('PR_RTF_SYNC_PREFIX_COUNT' ,mapi_prop_tag(PT_LONG, 0x1010)); -define('PR_RTF_SYNC_TRAILING_COUNT' ,mapi_prop_tag(PT_LONG, 0x1011)); -define('PR_ORIGINALLY_INTENDED_RECIP_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x1012)); -define('PR_NATIVE_BODY_INFO' ,mapi_prop_tag(PT_LONG, 0x1016)); - -define('PR_CONFLICT_ITEMS' ,mapi_prop_tag(PT_MV_BINARY, 0x1098)); - -/* - * Reserved 0x1100-0x1200 - */ - - -/* - * Message recipient properties - */ - -define('PR_CONTENT_INTEGRITY_CHECK' ,mapi_prop_tag(PT_BINARY, 0x0C00)); -define('PR_EXPLICIT_CONVERSION' ,mapi_prop_tag(PT_LONG, 0x0C01)); -define('PR_IPM_RETURN_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C02)); -define('PR_MESSAGE_TOKEN' ,mapi_prop_tag(PT_BINARY, 0x0C03)); -define('PR_NDR_REASON_CODE' ,mapi_prop_tag(PT_LONG, 0x0C04)); -define('PR_NDR_DIAG_CODE' ,mapi_prop_tag(PT_LONG, 0x0C05)); -define('PR_NON_RECEIPT_NOTIFICATION_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C06)); -define('PR_DELIVERY_POINT' ,mapi_prop_tag(PT_LONG, 0x0C07)); - -define('PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C08)); -define('PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT' ,mapi_prop_tag(PT_BINARY, 0x0C09)); -define('PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY' ,mapi_prop_tag(PT_BOOLEAN, 0x0C0A)); -define('PR_PHYSICAL_DELIVERY_MODE' ,mapi_prop_tag(PT_LONG, 0x0C0B)); -define('PR_PHYSICAL_DELIVERY_REPORT_REQUEST' ,mapi_prop_tag(PT_LONG, 0x0C0C)); -define('PR_PHYSICAL_FORWARDING_ADDRESS' ,mapi_prop_tag(PT_BINARY, 0x0C0D)); -define('PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C0E)); -define('PR_PHYSICAL_FORWARDING_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C0F)); -define('PR_PHYSICAL_RENDITION_ATTRIBUTES' ,mapi_prop_tag(PT_BINARY, 0x0C10)); -define('PR_PROOF_OF_DELIVERY' ,mapi_prop_tag(PT_BINARY, 0x0C11)); -define('PR_PROOF_OF_DELIVERY_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C12)); -define('PR_RECIPIENT_CERTIFICATE' ,mapi_prop_tag(PT_BINARY, 0x0C13)); -define('PR_RECIPIENT_NUMBER_FOR_ADVICE' ,mapi_prop_tag(PT_TSTRING, 0x0C14)); -define('PR_RECIPIENT_NUMBER_FOR_ADVICE_W' ,mapi_prop_tag(PT_UNICODE, 0x0C14)); -define('PR_RECIPIENT_NUMBER_FOR_ADVICE_A' ,mapi_prop_tag(PT_STRING8, 0x0C14)); -define('PR_RECIPIENT_TYPE' ,mapi_prop_tag(PT_LONG, 0x0C15)); -define('PR_REGISTERED_MAIL_TYPE' ,mapi_prop_tag(PT_LONG, 0x0C16)); -define('PR_REPLY_REQUESTED' ,mapi_prop_tag(PT_BOOLEAN, 0x0C17)); -define('PR_REQUESTED_DELIVERY_METHOD' ,mapi_prop_tag(PT_LONG, 0x0C18)); -define('PR_SENDER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0C19)); -define('PR_SENDER_NAME' ,mapi_prop_tag(PT_TSTRING, 0x0C1A)); -define('PR_SENDER_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x0C1A)); -define('PR_SENDER_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x0C1A)); -define('PR_SUPPLEMENTARY_INFO' ,mapi_prop_tag(PT_TSTRING, 0x0C1B)); -define('PR_SUPPLEMENTARY_INFO_W' ,mapi_prop_tag(PT_UNICODE, 0x0C1B)); -define('PR_SUPPLEMENTARY_INFO_A' ,mapi_prop_tag(PT_STRING8, 0x0C1B)); -define('PR_TYPE_OF_MTS_USER' ,mapi_prop_tag(PT_LONG, 0x0C1C)); -define('PR_SENDER_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x0C1D)); -define('PR_SENDER_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x0C1E)); -define('PR_SENDER_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x0C1E)); -define('PR_SENDER_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x0C1E)); -define('PR_SENDER_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x0C1F)); -define('PR_SENDER_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x0C1F)); -define('PR_SENDER_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x0C1F)); - -/* - * Message non-transmittable properties - */ - -/* - * The two tags, 'PR_MESSAGE_RECIPIENTS and 'PR_MESSAGE_ATTACHMENTS, - * are to be used in the exclude list passed to - * IMessage::CopyTo when the caller wants either the recipients or attachments - * of the message to not get copied. It is also used in the ProblemArray - * return from IMessage::CopyTo when an error is encountered copying them - */ - -define('PR_CURRENT_VERSION' ,mapi_prop_tag(PT_I8, 0x0E00)); -define('PR_DELETE_AFTER_SUBMIT' ,mapi_prop_tag(PT_BOOLEAN, 0x0E01)); -define('PR_DISPLAY_BCC' ,mapi_prop_tag(PT_TSTRING, 0x0E02)); -define('PR_DISPLAY_BCC_W' ,mapi_prop_tag(PT_UNICODE, 0x0E02)); -define('PR_DISPLAY_BCC_A' ,mapi_prop_tag(PT_STRING8, 0x0E02)); -define('PR_DISPLAY_CC' ,mapi_prop_tag(PT_TSTRING, 0x0E03)); -define('PR_DISPLAY_CC_W' ,mapi_prop_tag(PT_UNICODE, 0x0E03)); -define('PR_DISPLAY_CC_A' ,mapi_prop_tag(PT_STRING8, 0x0E03)); -define('PR_DISPLAY_TO' ,mapi_prop_tag(PT_TSTRING, 0x0E04)); -define('PR_DISPLAY_TO_W' ,mapi_prop_tag(PT_UNICODE, 0x0E04)); -define('PR_DISPLAY_TO_A' ,mapi_prop_tag(PT_STRING8, 0x0E04)); -define('PR_PARENT_DISPLAY' ,mapi_prop_tag(PT_TSTRING, 0x0E05)); -define('PR_PARENT_DISPLAY_W' ,mapi_prop_tag(PT_UNICODE, 0x0E05)); -define('PR_PARENT_DISPLAY_A' ,mapi_prop_tag(PT_STRING8, 0x0E05)); -define('PR_MESSAGE_DELIVERY_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x0E06)); -define('PR_MESSAGE_FLAGS' ,mapi_prop_tag(PT_LONG, 0x0E07)); -define('PR_MESSAGE_SIZE' ,mapi_prop_tag(PT_LONG, 0x0E08)); -define('PR_MESSAGE_SIZE_EXTENDED' ,mapi_prop_tag(PT_LONGLONG, 0x0E08)); -define('PR_PARENT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0E09)); -define('PR_SENTMAIL_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0E0A)); -define('PR_CORRELATE' ,mapi_prop_tag(PT_BOOLEAN, 0x0E0C)); -define('PR_CORRELATE_MTSID' ,mapi_prop_tag(PT_BINARY, 0x0E0D)); -define('PR_DISCRETE_VALUES' ,mapi_prop_tag(PT_BOOLEAN, 0x0E0E)); -define('PR_RESPONSIBILITY' ,mapi_prop_tag(PT_BOOLEAN, 0x0E0F)); -define('PR_SPOOLER_STATUS' ,mapi_prop_tag(PT_LONG, 0x0E10)); -define('PR_TRANSPORT_STATUS' ,mapi_prop_tag(PT_LONG, 0x0E11)); -define('PR_MESSAGE_RECIPIENTS' ,mapi_prop_tag(PT_OBJECT, 0x0E12)); -define('PR_MESSAGE_ATTACHMENTS' ,mapi_prop_tag(PT_OBJECT, 0x0E13)); -define('PR_SUBMIT_FLAGS' ,mapi_prop_tag(PT_LONG, 0x0E14)); -define('PR_RECIPIENT_STATUS' ,mapi_prop_tag(PT_LONG, 0x0E15)); -define('PR_TRANSPORT_KEY' ,mapi_prop_tag(PT_LONG, 0x0E16)); -define('PR_MSG_STATUS' ,mapi_prop_tag(PT_LONG, 0x0E17)); -define('PR_MESSAGE_DOWNLOAD_TIME' ,mapi_prop_tag(PT_LONG, 0x0E18)); -define('PR_CREATION_VERSION' ,mapi_prop_tag(PT_I8, 0x0E19)); -define('PR_MODIFY_VERSION' ,mapi_prop_tag(PT_I8, 0x0E1A)); -define('PR_HASATTACH' ,mapi_prop_tag(PT_BOOLEAN, 0x0E1B)); -define('PR_BODY_CRC' ,mapi_prop_tag(PT_LONG, 0x0E1C)); -define('PR_NORMALIZED_SUBJECT' ,mapi_prop_tag(PT_TSTRING, 0x0E1D)); -define('PR_NORMALIZED_SUBJECT_W' ,mapi_prop_tag(PT_UNICODE, 0x0E1D)); -define('PR_NORMALIZED_SUBJECT_A' ,mapi_prop_tag(PT_STRING8, 0x0E1D)); -define('PR_RTF_IN_SYNC' ,mapi_prop_tag(PT_BOOLEAN, 0x0E1F)); -define('PR_ATTACH_SIZE' ,mapi_prop_tag(PT_LONG, 0x0E20)); -define('PR_ATTACH_NUM' ,mapi_prop_tag(PT_LONG, 0x0E21)); -define('PR_PREPROCESS' ,mapi_prop_tag(PT_BOOLEAN, 0x0E22)); - -/* 'PR_ORIGINAL_DISPLAY_TO, _CC, and _BCC moved to transmittible range 03/09/95 */ - -define('PR_ORIGINATING_MTA_CERTIFICATE' ,mapi_prop_tag(PT_BINARY, 0x0E25)); -define('PR_PROOF_OF_SUBMISSION' ,mapi_prop_tag(PT_BINARY, 0x0E26)); - - -/* - * The range of non-message and non-recipient property IDs (0x3000 - 0x3FFF)); is - * further broken down into ranges to make assigning new property IDs easier. - * - * From To Kind of property - * -------------------------------- - * 3000 32FF MAPI_defined common property - * 3200 33FF MAPI_defined form property - * 3400 35FF MAPI_defined message store property - * 3600 36FF MAPI_defined Folder or AB Container property - * 3700 38FF MAPI_defined attachment property - * 3900 39FF MAPI_defined address book property - * 3A00 3BFF MAPI_defined mailuser property - * 3C00 3CFF MAPI_defined DistList property - * 3D00 3DFF MAPI_defined Profile Section property - * 3E00 3EFF MAPI_defined Status property - * 3F00 3FFF MAPI_defined display table property - */ - -/* - * Properties common to numerous MAPI objects. - * - * Those properties that can appear on messages are in the - * non-transmittable range for messages. They start at the high - * end of that range and work down. - * - * Properties that never appear on messages are defined in the common - * property range (see above));. - */ - -/* - * properties that are common to multiple objects (including message objects)); - * -- these ids are in the non-transmittable range - */ - -define('PR_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0FFF)); -define('PR_OBJECT_TYPE' ,mapi_prop_tag(PT_LONG, 0x0FFE)); -define('PR_ICON' ,mapi_prop_tag(PT_BINARY, 0x0FFD)); -define('PR_MINI_ICON' ,mapi_prop_tag(PT_BINARY, 0x0FFC)); -define('PR_STORE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x0FFB)); -define('PR_STORE_RECORD_KEY' ,mapi_prop_tag(PT_BINARY, 0x0FFA)); -define('PR_RECORD_KEY' ,mapi_prop_tag(PT_BINARY, 0x0FF9)); -define('PR_MAPPING_SIGNATURE' ,mapi_prop_tag(PT_BINARY, 0x0FF8)); -define('PR_ACCESS_LEVEL' ,mapi_prop_tag(PT_LONG, 0x0FF7)); -define('PR_INSTANCE_KEY' ,mapi_prop_tag(PT_BINARY, 0x0FF6)); -define('PR_ROW_TYPE' ,mapi_prop_tag(PT_LONG, 0x0FF5)); -define('PR_ACCESS' ,mapi_prop_tag(PT_LONG, 0x0FF4)); - -/* - * properties that are common to multiple objects (usually not including message objects)); - * -- these ids are in the transmittable range - */ - -define('PR_ROWID' ,mapi_prop_tag(PT_LONG, 0x3000)); -define('PR_DISPLAY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3001)); -define('PR_DISPLAY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3001)); -define('PR_DISPLAY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3001)); -define('PR_ADDRTYPE' ,mapi_prop_tag(PT_TSTRING, 0x3002)); -define('PR_ADDRTYPE_W' ,mapi_prop_tag(PT_UNICODE, 0x3002)); -define('PR_ADDRTYPE_A' ,mapi_prop_tag(PT_STRING8, 0x3002)); -define('PR_EMAIL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x3003)); -define('PR_EMAIL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x3003)); -define('PR_EMAIL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x3003)); -define('PR_COMMENT' ,mapi_prop_tag(PT_TSTRING, 0x3004)); -define('PR_COMMENT_W' ,mapi_prop_tag(PT_UNICODE, 0x3004)); -define('PR_COMMENT_A' ,mapi_prop_tag(PT_STRING8, 0x3004)); -define('PR_DEPTH' ,mapi_prop_tag(PT_LONG, 0x3005)); -define('PR_PROVIDER_DISPLAY' ,mapi_prop_tag(PT_TSTRING, 0x3006)); -define('PR_PROVIDER_DISPLAY_W' ,mapi_prop_tag(PT_UNICODE, 0x3006)); -define('PR_PROVIDER_DISPLAY_A' ,mapi_prop_tag(PT_STRING8, 0x3006)); -define('PR_CREATION_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x3007)); -define('PR_LAST_MODIFICATION_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x3008)); -define('PR_RESOURCE_FLAGS' ,mapi_prop_tag(PT_LONG, 0x3009)); -define('PR_PROVIDER_DLL_NAME' ,mapi_prop_tag(PT_TSTRING, 0x300A)); -define('PR_PROVIDER_DLL_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x300A)); -define('PR_PROVIDER_DLL_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x300A)); -define('PR_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x300B)); -define('PR_PROVIDER_UID' ,mapi_prop_tag(PT_BINARY, 0x300C)); -define('PR_PROVIDER_ORDINAL' ,mapi_prop_tag(PT_LONG, 0x300D)); - -/* - * MAPI Form properties - */ -define('PR_FORM_VERSION' ,mapi_prop_tag(PT_TSTRING, 0x3301)); -define('PR_FORM_VERSION_W' ,mapi_prop_tag(PT_UNICODE, 0x3301)); -define('PR_FORM_VERSION_A' ,mapi_prop_tag(PT_STRING8, 0x3301)); -define('PR_FORM_CLSID' ,mapi_prop_tag(PT_CLSID, 0x3302)); -define('PR_FORM_CONTACT_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3303)); -define('PR_FORM_CONTACT_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3303)); -define('PR_FORM_CONTACT_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3303)); -define('PR_FORM_CATEGORY' ,mapi_prop_tag(PT_TSTRING, 0x3304)); -define('PR_FORM_CATEGORY_W' ,mapi_prop_tag(PT_UNICODE, 0x3304)); -define('PR_FORM_CATEGORY_A' ,mapi_prop_tag(PT_STRING8, 0x3304)); -define('PR_FORM_CATEGORY_SUB' ,mapi_prop_tag(PT_TSTRING, 0x3305)); -define('PR_FORM_CATEGORY_SUB_W' ,mapi_prop_tag(PT_UNICODE, 0x3305)); -define('PR_FORM_CATEGORY_SUB_A' ,mapi_prop_tag(PT_STRING8, 0x3305)); -define('PR_FORM_HOST_MAP' ,mapi_prop_tag(PT_MV_LONG, 0x3306)); -define('PR_FORM_HIDDEN' ,mapi_prop_tag(PT_BOOLEAN, 0x3307)); -define('PR_FORM_DESIGNER_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3308)); -define('PR_FORM_DESIGNER_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3308)); -define('PR_FORM_DESIGNER_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3308)); -define('PR_FORM_DESIGNER_GUID' ,mapi_prop_tag(PT_CLSID, 0x3309)); -define('PR_FORM_MESSAGE_BEHAVIOR' ,mapi_prop_tag(PT_LONG, 0x330A)); - -/* - * Message store properties - */ - -define('PR_DEFAULT_STORE' ,mapi_prop_tag(PT_BOOLEAN, 0x3400)); -define('PR_STORE_SUPPORT_MASK' ,mapi_prop_tag(PT_LONG, 0x340D)); -define('PR_STORE_STATE' ,mapi_prop_tag(PT_LONG, 0x340E)); - -define('PR_IPM_SUBTREE_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3410)); -define('PR_IPM_OUTBOX_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3411)); -define('PR_IPM_WASTEBASKET_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3412)); -define('PR_IPM_SENTMAIL_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3413)); -define('PR_MDB_PROVIDER' ,mapi_prop_tag(PT_BINARY, 0x3414)); -define('PR_RECEIVE_FOLDER_SETTINGS' ,mapi_prop_tag(PT_OBJECT, 0x3415)); - -define('PR_VALID_FOLDER_MASK' ,mapi_prop_tag(PT_LONG, 0x35DF)); -define('PR_IPM_SUBTREE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E0)); - -define('PR_IPM_OUTBOX_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E2)); -define('PR_IPM_WASTEBASKET_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E3)); -define('PR_IPM_SENTMAIL_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E4)); -define('PR_VIEWS_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E5)); -define('PR_COMMON_VIEWS_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E6)); -define('PR_FINDER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x35E7)); -define('PR_IPM_FAVORITES_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x6630)); -define('PR_IPM_PUBLIC_FOLDERS_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x6631)); - - -/* Proptags 0x35E8-0x35FF reserved for folders "guaranteed" by 'PR_VALID_FOLDER_MASK */ - - -/* - * Folder and AB Container properties - */ - -define('PR_CONTAINER_FLAGS' ,mapi_prop_tag(PT_LONG, 0x3600)); -define('PR_FOLDER_TYPE' ,mapi_prop_tag(PT_LONG, 0x3601)); -define('PR_CONTENT_COUNT' ,mapi_prop_tag(PT_LONG, 0x3602)); -define('PR_CONTENT_UNREAD' ,mapi_prop_tag(PT_LONG, 0x3603)); -define('PR_CREATE_TEMPLATES' ,mapi_prop_tag(PT_OBJECT, 0x3604)); -define('PR_DETAILS_TABLE' ,mapi_prop_tag(PT_OBJECT, 0x3605)); -define('PR_SEARCH' ,mapi_prop_tag(PT_OBJECT, 0x3607)); -define('PR_SELECTABLE' ,mapi_prop_tag(PT_BOOLEAN, 0x3609)); -define('PR_SUBFOLDERS' ,mapi_prop_tag(PT_BOOLEAN, 0x360A)); -define('PR_STATUS' ,mapi_prop_tag(PT_LONG, 0x360B)); -define('PR_ANR' ,mapi_prop_tag(PT_TSTRING, 0x360C)); -define('PR_ANR_W' ,mapi_prop_tag(PT_UNICODE, 0x360C)); -define('PR_ANR_A' ,mapi_prop_tag(PT_STRING8, 0x360C)); -define('PR_CONTENTS_SORT_ORDER' ,mapi_prop_tag(PT_MV_LONG, 0x360D)); -define('PR_CONTAINER_HIERARCHY' ,mapi_prop_tag(PT_OBJECT, 0x360E)); -define('PR_CONTAINER_CONTENTS' ,mapi_prop_tag(PT_OBJECT, 0x360F)); -define('PR_FOLDER_ASSOCIATED_CONTENTS' ,mapi_prop_tag(PT_OBJECT, 0x3610)); -define('PR_DEF_CREATE_DL' ,mapi_prop_tag(PT_BINARY, 0x3611)); -define('PR_DEF_CREATE_MAILUSER' ,mapi_prop_tag(PT_BINARY, 0x3612)); -define('PR_CONTAINER_CLASS' ,mapi_prop_tag(PT_TSTRING, 0x3613)); -define('PR_CONTAINER_CLASS_W' ,mapi_prop_tag(PT_UNICODE, 0x3613)); -define('PR_CONTAINER_CLASS_A' ,mapi_prop_tag(PT_STRING8, 0x3613)); -define('PR_CONTAINER_MODIFY_VERSION' ,mapi_prop_tag(PT_I8, 0x3614)); -define('PR_AB_PROVIDER_ID' ,mapi_prop_tag(PT_BINARY, 0x3615)); -define('PR_DEFAULT_VIEW_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x3616)); -define('PR_ASSOC_CONTENT_COUNT' ,mapi_prop_tag(PT_LONG, 0x3617)); -define('PR_EXTENDED_FOLDER_FLAGS' ,mapi_prop_tag(PT_BINARY, 0x36DA)); - -define('PR_RIGHTS' ,mapi_prop_tag(PT_LONG, 0x6639)); - -/* Reserved 0x36C0-0x36FF */ - -/* - * Attachment properties - */ - -define('PR_ATTACHMENT_X400_PARAMETERS' ,mapi_prop_tag(PT_BINARY, 0x3700)); -define('PR_ATTACH_DATA_OBJ' ,mapi_prop_tag(PT_OBJECT, 0x3701)); -define('PR_ATTACH_DATA_BIN' ,mapi_prop_tag(PT_BINARY, 0x3701)); -define('PR_ATTACH_CONTENT_ID' ,mapi_prop_tag(PT_STRING8, 0x3712)); -define('PR_ATTACH_CONTENT_ID_W' ,mapi_prop_tag(PT_UNICODE, 0x3712)); -define('PR_ATTACH_CONTENT_LOCATION' ,mapi_prop_tag(PT_STRING8, 0x3713)); -define('PR_ATTACH_ENCODING' ,mapi_prop_tag(PT_BINARY, 0x3702)); -define('PR_ATTACH_EXTENSION' ,mapi_prop_tag(PT_TSTRING, 0x3703)); -define('PR_ATTACH_EXTENSION_W' ,mapi_prop_tag(PT_UNICODE, 0x3703)); -define('PR_ATTACH_EXTENSION_A' ,mapi_prop_tag(PT_STRING8, 0x3703)); -define('PR_ATTACH_FILENAME' ,mapi_prop_tag(PT_TSTRING, 0x3704)); -define('PR_ATTACH_FILENAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3704)); -define('PR_ATTACH_FILENAME_A' ,mapi_prop_tag(PT_STRING8, 0x3704)); -define('PR_ATTACH_METHOD' ,mapi_prop_tag(PT_LONG, 0x3705)); -define('PR_ATTACH_LONG_FILENAME' ,mapi_prop_tag(PT_TSTRING, 0x3707)); -define('PR_ATTACH_LONG_FILENAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3707)); -define('PR_ATTACH_LONG_FILENAME_A' ,mapi_prop_tag(PT_STRING8, 0x3707)); -define('PR_ATTACH_PATHNAME' ,mapi_prop_tag(PT_TSTRING, 0x3708)); -define('PR_ATTACH_PATHNAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3708)); -define('PR_ATTACH_PATHNAME_A' ,mapi_prop_tag(PT_STRING8, 0x3708)); -define('PR_ATTACH_RENDERING' ,mapi_prop_tag(PT_BINARY, 0x3709)); -define('PR_ATTACH_TAG' ,mapi_prop_tag(PT_BINARY, 0x370A)); -define('PR_RENDERING_POSITION' ,mapi_prop_tag(PT_LONG, 0x370B)); -define('PR_ATTACH_TRANSPORT_NAME' ,mapi_prop_tag(PT_TSTRING, 0x370C)); -define('PR_ATTACH_TRANSPORT_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x370C)); -define('PR_ATTACH_TRANSPORT_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x370C)); -define('PR_ATTACH_LONG_PATHNAME' ,mapi_prop_tag(PT_TSTRING, 0x370D)); -define('PR_ATTACH_LONG_PATHNAME_W' ,mapi_prop_tag(PT_UNICODE, 0x370D)); -define('PR_ATTACH_LONG_PATHNAME_A' ,mapi_prop_tag(PT_STRING8, 0x370D)); -define('PR_ATTACH_MIME_TAG' ,mapi_prop_tag(PT_TSTRING, 0x370E)); -define('PR_ATTACH_MIME_TAG_W' ,mapi_prop_tag(PT_UNICODE, 0x370E)); -define('PR_ATTACH_MIME_TAG_A' ,mapi_prop_tag(PT_STRING8, 0x370E)); -define('PR_ATTACH_ADDITIONAL_INFO' ,mapi_prop_tag(PT_BINARY, 0x370F)); -define('PR_ATTACHMENT_FLAGS' ,mapi_prop_tag(PT_LONG, 0x7FFD)); -define('PR_ATTACHMENT_HIDDEN' ,mapi_prop_tag(PT_BOOLEAN, 0x7FFE)); -define('PR_ATTACHMENT_LINKID' ,mapi_prop_tag(PT_LONG, 0x7FFA)); -define('PR_ATTACH_FLAGS' ,mapi_prop_tag(PT_LONG, 0x3714)); -define('PR_EXCEPTION_STARTTIME' ,mapi_prop_tag(PT_SYSTIME, 0x7FFB)); -define('PR_EXCEPTION_ENDTIME' ,mapi_prop_tag(PT_SYSTIME, 0x7FFC)); - -/* - * AB Object properties - */ - -define('PR_DISPLAY_TYPE' ,mapi_prop_tag(PT_LONG, 0x3900)); -define('PR_DISPLAY_TYPE_EX' ,mapi_prop_tag(PT_LONG, 0x3905)); -define('PR_TEMPLATEID' ,mapi_prop_tag(PT_BINARY, 0x3902)); -define('PR_PRIMARY_CAPABILITY' ,mapi_prop_tag(PT_BINARY, 0x3904)); - - -/* - * Mail user properties - */ -define('PR_7BIT_DISPLAY_NAME' ,mapi_prop_tag(PT_STRING8, 0x39FF)); -define('PR_ACCOUNT' ,mapi_prop_tag(PT_TSTRING, 0x3A00)); -define('PR_ACCOUNT_W' ,mapi_prop_tag(PT_UNICODE, 0x3A00)); -define('PR_ACCOUNT_A' ,mapi_prop_tag(PT_STRING8, 0x3A00)); -define('PR_ALTERNATE_RECIPIENT' ,mapi_prop_tag(PT_BINARY, 0x3A01)); -define('PR_CALLBACK_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A02)); -define('PR_CALLBACK_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A02)); -define('PR_CALLBACK_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A02)); -define('PR_CONVERSION_PROHIBITED' ,mapi_prop_tag(PT_BOOLEAN, 0x3A03)); -define('PR_DISCLOSE_RECIPIENTS' ,mapi_prop_tag(PT_BOOLEAN, 0x3A04)); -define('PR_GENERATION' ,mapi_prop_tag(PT_TSTRING, 0x3A05)); -define('PR_GENERATION_W' ,mapi_prop_tag(PT_UNICODE, 0x3A05)); -define('PR_GENERATION_A' ,mapi_prop_tag(PT_STRING8, 0x3A05)); -define('PR_GIVEN_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A06)); -define('PR_GIVEN_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A06)); -define('PR_GIVEN_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A06)); -define('PR_GOVERNMENT_ID_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A07)); -define('PR_GOVERNMENT_ID_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A07)); -define('PR_GOVERNMENT_ID_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A07)); -define('PR_BUSINESS_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A08)); -define('PR_BUSINESS_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A08)); -define('PR_BUSINESS_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A08)); -define('PR_OFFICE_TELEPHONE_NUMBER' ,PR_BUSINESS_TELEPHONE_NUMBER); -define('PR_OFFICE_TELEPHONE_NUMBER_W' ,PR_BUSINESS_TELEPHONE_NUMBER_W); -define('PR_OFFICE_TELEPHONE_NUMBER_A' ,PR_BUSINESS_TELEPHONE_NUMBER_A); -define('PR_HOME_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A09)); -define('PR_HOME_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A09)); -define('PR_HOME_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A09)); -define('PR_INITIALS' ,mapi_prop_tag(PT_TSTRING, 0x3A0A)); -define('PR_INITIALS_W' ,mapi_prop_tag(PT_UNICODE, 0x3A0A)); -define('PR_INITIALS_A' ,mapi_prop_tag(PT_STRING8, 0x3A0A)); -define('PR_KEYWORD' ,mapi_prop_tag(PT_TSTRING, 0x3A0B)); -define('PR_KEYWORD_W' ,mapi_prop_tag(PT_UNICODE, 0x3A0B)); -define('PR_KEYWORD_A' ,mapi_prop_tag(PT_STRING8, 0x3A0B)); -define('PR_LANGUAGE' ,mapi_prop_tag(PT_TSTRING, 0x3A0C)); -define('PR_LANGUAGE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A0C)); -define('PR_LANGUAGE_A' ,mapi_prop_tag(PT_STRING8, 0x3A0C)); -define('PR_LOCATION' ,mapi_prop_tag(PT_TSTRING, 0x3A0D)); -define('PR_LOCATION_W' ,mapi_prop_tag(PT_UNICODE, 0x3A0D)); -define('PR_LOCATION_A' ,mapi_prop_tag(PT_STRING8, 0x3A0D)); -define('PR_MAIL_PERMISSION' ,mapi_prop_tag(PT_BOOLEAN, 0x3A0E)); -define('PR_MHS_COMMON_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A0F)); -define('PR_MHS_COMMON_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A0F)); -define('PR_MHS_COMMON_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A0F)); -define('PR_ORGANIZATIONAL_ID_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A10)); -define('PR_ORGANIZATIONAL_ID_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A10)); -define('PR_ORGANIZATIONAL_ID_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A10)); -define('PR_SURNAME' ,mapi_prop_tag(PT_TSTRING, 0x3A11)); -define('PR_SURNAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A11)); -define('PR_SURNAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A11)); -define('PR_ORIGINAL_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x3A12)); -define('PR_ORIGINAL_DISPLAY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A13)); -define('PR_ORIGINAL_DISPLAY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A13)); -define('PR_ORIGINAL_DISPLAY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A13)); -define('PR_ORIGINAL_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3A14)); -define('PR_POSTAL_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x3A15)); -define('PR_POSTAL_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x3A15)); -define('PR_POSTAL_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x3A15)); -define('PR_COMPANY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A16)); -define('PR_COMPANY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A16)); -define('PR_COMPANY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A16)); -define('PR_TITLE' ,mapi_prop_tag(PT_TSTRING, 0x3A17)); -define('PR_TITLE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A17)); -define('PR_TITLE_A' ,mapi_prop_tag(PT_STRING8, 0x3A17)); -define('PR_DEPARTMENT_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A18)); -define('PR_DEPARTMENT_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A18)); -define('PR_DEPARTMENT_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A18)); -define('PR_OFFICE_LOCATION' ,mapi_prop_tag(PT_TSTRING, 0x3A19)); -define('PR_OFFICE_LOCATION_W' ,mapi_prop_tag(PT_UNICODE, 0x3A19)); -define('PR_OFFICE_LOCATION_A' ,mapi_prop_tag(PT_STRING8, 0x3A19)); -define('PR_PRIMARY_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1A)); -define('PR_PRIMARY_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1A)); -define('PR_PRIMARY_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1A)); -define('PR_BUSINESS2_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1B)); -define('PR_BUSINESS2_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1B)); -define('PR_BUSINESS2_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1B)); -define('PR_OFFICE2_TELEPHONE_NUMBER' ,PR_BUSINESS2_TELEPHONE_NUMBER); -define('PR_OFFICE2_TELEPHONE_NUMBER_W' ,PR_BUSINESS2_TELEPHONE_NUMBER_W); -define('PR_OFFICE2_TELEPHONE_NUMBER_A' ,PR_BUSINESS2_TELEPHONE_NUMBER_A); -define('PR_MOBILE_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1C)); -define('PR_MOBILE_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1C)); -define('PR_MOBILE_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1C)); -define('PR_CELLULAR_TELEPHONE_NUMBER' ,PR_MOBILE_TELEPHONE_NUMBER); -define('PR_CELLULAR_TELEPHONE_NUMBER_W' ,PR_MOBILE_TELEPHONE_NUMBER_W); -define('PR_CELLULAR_TELEPHONE_NUMBER_A' ,PR_MOBILE_TELEPHONE_NUMBER_A); -define('PR_RADIO_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1D)); -define('PR_RADIO_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1D)); -define('PR_RADIO_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1D)); -define('PR_CAR_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1E)); -define('PR_CAR_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1E)); -define('PR_CAR_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1E)); -define('PR_OTHER_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A1F)); -define('PR_OTHER_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A1F)); -define('PR_OTHER_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A1F)); -define('PR_TRANSMITABLE_DISPLAY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A20)); -define('PR_TRANSMITABLE_DISPLAY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A20)); -define('PR_TRANSMITABLE_DISPLAY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A20)); -define('PR_PAGER_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A21)); -define('PR_PAGER_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A21)); -define('PR_PAGER_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A21)); -define('PR_BEEPER_TELEPHONE_NUMBER' ,PR_PAGER_TELEPHONE_NUMBER); -define('PR_BEEPER_TELEPHONE_NUMBER_W' ,PR_PAGER_TELEPHONE_NUMBER_W); -define('PR_BEEPER_TELEPHONE_NUMBER_A' ,PR_PAGER_TELEPHONE_NUMBER_A); -define('PR_USER_CERTIFICATE' ,mapi_prop_tag(PT_BINARY, 0x3A22)); -define('PR_PRIMARY_FAX_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A23)); -define('PR_PRIMARY_FAX_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A23)); -define('PR_PRIMARY_FAX_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A23)); -define('PR_BUSINESS_FAX_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A24)); -define('PR_BUSINESS_FAX_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A24)); -define('PR_BUSINESS_FAX_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A24)); -define('PR_HOME_FAX_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A25)); -define('PR_HOME_FAX_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A25)); -define('PR_HOME_FAX_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A25)); -define('PR_COUNTRY' ,mapi_prop_tag(PT_TSTRING, 0x3A26)); -define('PR_COUNTRY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A26)); -define('PR_COUNTRY_A' ,mapi_prop_tag(PT_STRING8, 0x3A26)); -define('PR_BUSINESS_ADDRESS_COUNTRY' ,PR_COUNTRY); -define('PR_BUSINESS_ADDRESS_COUNTRY_W' ,PR_COUNTRY_W); -define('PR_BUSINESS_ADDRESS_COUNTRY_A' ,PR_COUNTRY_A); - -define('PR_FLAG_STATUS' ,mapi_prop_tag(PT_LONG, 0x1090)); -define('PR_FLAG_COMPLETE_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x1091)); -define('PR_FLAG_ICON' ,mapi_prop_tag(PT_LONG, 0x1095)); -define('PR_BLOCK_STATUS' ,mapi_prop_tag(PT_LONG, 0x1096)); - -define('PR_LOCALITY' ,mapi_prop_tag(PT_TSTRING, 0x3A27)); -define('PR_LOCALITY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A27)); -define('PR_LOCALITY_A' ,mapi_prop_tag(PT_STRING8, 0x3A27)); -define('PR_BUSINESS_ADDRESS_CITY' ,PR_LOCALITY); -define('PR_BUSINESS_ADDRESS_CITY_W' ,PR_LOCALITY_W); -define('PR_BUSINESS_ADDRESS_CITY_A' ,PR_LOCALITY_A); - -define('PR_STATE_OR_PROVINCE' ,mapi_prop_tag(PT_TSTRING, 0x3A28)); -define('PR_STATE_OR_PROVINCE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A28)); -define('PR_STATE_OR_PROVINCE_A' ,mapi_prop_tag(PT_STRING8, 0x3A28)); -define('PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE' ,PR_STATE_OR_PROVINCE); -define('PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W' ,PR_STATE_OR_PROVINCE_W); -define('PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A' ,PR_STATE_OR_PROVINCE_A); - -define('PR_STREET_ADDRESS' ,mapi_prop_tag(PT_TSTRING, 0x3A29)); -define('PR_STREET_ADDRESS_W' ,mapi_prop_tag(PT_UNICODE, 0x3A29)); -define('PR_STREET_ADDRESS_A' ,mapi_prop_tag(PT_STRING8, 0x3A29)); -define('PR_BUSINESS_ADDRESS_STREET' ,PR_STREET_ADDRESS); -define('PR_BUSINESS_ADDRESS_STREET_W' ,PR_STREET_ADDRESS_W); -define('PR_BUSINESS_ADDRESS_STREET_A' ,PR_STREET_ADDRESS_A); - -define('PR_POSTAL_CODE' ,mapi_prop_tag(PT_TSTRING, 0x3A2A)); -define('PR_POSTAL_CODE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2A)); -define('PR_POSTAL_CODE_A' ,mapi_prop_tag(PT_STRING8, 0x3A2A)); -define('PR_BUSINESS_ADDRESS_POSTAL_CODE' ,PR_POSTAL_CODE); -define('PR_BUSINESS_ADDRESS_POSTAL_CODE_W' ,PR_POSTAL_CODE_W); -define('PR_BUSINESS_ADDRESS_POSTAL_CODE_A' ,PR_POSTAL_CODE_A); - - -define('PR_POST_OFFICE_BOX' ,mapi_prop_tag(PT_TSTRING, 0x3A2B)); -define('PR_POST_OFFICE_BOX_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2B)); -define('PR_POST_OFFICE_BOX_A' ,mapi_prop_tag(PT_STRING8, 0x3A2B)); -define('PR_BUSINESS_ADDRESS_POST_OFFICE_BOX' ,PR_POST_OFFICE_BOX); -define('PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_W' ,PR_POST_OFFICE_BOX_W); -define('PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_A' ,PR_POST_OFFICE_BOX_A); - - -define('PR_TELEX_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A2C)); -define('PR_TELEX_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2C)); -define('PR_TELEX_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A2C)); -define('PR_ISDN_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A2D)); -define('PR_ISDN_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2D)); -define('PR_ISDN_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A2D)); -define('PR_ASSISTANT_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A2E)); -define('PR_ASSISTANT_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2E)); -define('PR_ASSISTANT_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A2E)); -define('PR_HOME2_TELEPHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A2F)); -define('PR_HOME2_TELEPHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A2F)); -define('PR_HOME2_TELEPHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A2F)); -define('PR_ASSISTANT' ,mapi_prop_tag(PT_TSTRING, 0x3A30)); -define('PR_ASSISTANT_W' ,mapi_prop_tag(PT_UNICODE, 0x3A30)); -define('PR_ASSISTANT_A' ,mapi_prop_tag(PT_STRING8, 0x3A30)); -define('PR_SEND_RICH_INFO' ,mapi_prop_tag(PT_BOOLEAN, 0x3A40)); -define('PR_WEDDING_ANNIVERSARY' ,mapi_prop_tag(PT_SYSTIME, 0x3A41)); -define('PR_BIRTHDAY' ,mapi_prop_tag(PT_SYSTIME, 0x3A42)); - - -define('PR_HOBBIES' ,mapi_prop_tag(PT_TSTRING, 0x3A43)); -define('PR_HOBBIES_W' ,mapi_prop_tag(PT_UNICODE, 0x3A43)); -define('PR_HOBBIES_A' ,mapi_prop_tag(PT_STRING8, 0x3A43)); - -define('PR_MIDDLE_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A44)); -define('PR_MIDDLE_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A44)); -define('PR_MIDDLE_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A44)); - -define('PR_DISPLAY_NAME_PREFIX' ,mapi_prop_tag(PT_TSTRING, 0x3A45)); -define('PR_DISPLAY_NAME_PREFIX_W' ,mapi_prop_tag(PT_UNICODE, 0x3A45)); -define('PR_DISPLAY_NAME_PREFIX_A' ,mapi_prop_tag(PT_STRING8, 0x3A45)); - -define('PR_PROFESSION' ,mapi_prop_tag(PT_TSTRING, 0x3A46)); -define('PR_PROFESSION_W' ,mapi_prop_tag(PT_UNICODE, 0x3A46)); -define('PR_PROFESSION_A' ,mapi_prop_tag(PT_STRING8, 0x3A46)); - -define('PR_PREFERRED_BY_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A47)); -define('PR_PREFERRED_BY_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A47)); -define('PR_PREFERRED_BY_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A47)); - -define('PR_SPOUSE_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A48)); -define('PR_SPOUSE_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A48)); -define('PR_SPOUSE_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A48)); - -define('PR_COMPUTER_NETWORK_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A49)); -define('PR_COMPUTER_NETWORK_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A49)); -define('PR_COMPUTER_NETWORK_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A49)); - -define('PR_CUSTOMER_ID' ,mapi_prop_tag(PT_TSTRING, 0x3A4A)); -define('PR_CUSTOMER_ID_W' ,mapi_prop_tag(PT_UNICODE, 0x3A4A)); -define('PR_CUSTOMER_ID_A' ,mapi_prop_tag(PT_STRING8, 0x3A4A)); - -define('PR_TTYTDD_PHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A4B)); -define('PR_TTYTDD_PHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A4B)); -define('PR_TTYTDD_PHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A4B)); - -define('PR_FTP_SITE' ,mapi_prop_tag(PT_TSTRING, 0x3A4C)); -define('PR_FTP_SITE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A4C)); -define('PR_FTP_SITE_A' ,mapi_prop_tag(PT_STRING8, 0x3A4C)); - -define('PR_GENDER' ,mapi_prop_tag(PT_SHORT, 0x3A4D)); - -define('PR_MANAGER_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3A4E)); -define('PR_MANAGER_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A4E)); -define('PR_MANAGER_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A4E)); - -define('PR_NICKNAME' ,mapi_prop_tag(PT_TSTRING, 0x3A4F)); -define('PR_NICKNAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3A4F)); -define('PR_NICKNAME_A' ,mapi_prop_tag(PT_STRING8, 0x3A4F)); - -define('PR_PERSONAL_HOME_PAGE' ,mapi_prop_tag(PT_TSTRING, 0x3A50)); -define('PR_PERSONAL_HOME_PAGE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A50)); -define('PR_PERSONAL_HOME_PAGE_A' ,mapi_prop_tag(PT_STRING8, 0x3A50)); - - -define('PR_BUSINESS_HOME_PAGE' ,mapi_prop_tag(PT_TSTRING, 0x3A51)); -define('PR_BUSINESS_HOME_PAGE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A51)); -define('PR_BUSINESS_HOME_PAGE_A' ,mapi_prop_tag(PT_STRING8, 0x3A51)); - -define('PR_CONTACT_VERSION' ,mapi_prop_tag(PT_CLSID, 0x3A52)); -define('PR_CONTACT_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, 0x3A53)); - -define('PR_CONTACT_ADDRTYPES' ,mapi_prop_tag(PT_MV_TSTRING, 0x3A54)); -define('PR_CONTACT_ADDRTYPES_W' ,mapi_prop_tag(PT_MV_UNICODE, 0x3A54)); -define('PR_CONTACT_ADDRTYPES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x3A54)); - -define('PR_CONTACT_DEFAULT_ADDRESS_INDEX' ,mapi_prop_tag(PT_LONG, 0x3A55)); - -define('PR_CONTACT_EMAIL_ADDRESSES' ,mapi_prop_tag(PT_MV_TSTRING, 0x3A56)); -define('PR_CONTACT_EMAIL_ADDRESSES_W' ,mapi_prop_tag(PT_MV_UNICODE, 0x3A56)); -define('PR_CONTACT_EMAIL_ADDRESSES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x3A56)); -define('PR_ATTACHMENT_CONTACTPHOTO' ,mapi_prop_tag(PT_BOOLEAN, 0x7FFF)); - - -define('PR_COMPANY_MAIN_PHONE_NUMBER' ,mapi_prop_tag(PT_TSTRING, 0x3A57)); -define('PR_COMPANY_MAIN_PHONE_NUMBER_W' ,mapi_prop_tag(PT_UNICODE, 0x3A57)); -define('PR_COMPANY_MAIN_PHONE_NUMBER_A' ,mapi_prop_tag(PT_STRING8, 0x3A57)); - -define('PR_CHILDRENS_NAMES' ,mapi_prop_tag(PT_MV_TSTRING, 0x3A58)); -define('PR_CHILDRENS_NAMES_W' ,mapi_prop_tag(PT_MV_UNICODE, 0x3A58)); -define('PR_CHILDRENS_NAMES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x3A58)); - - - -define('PR_HOME_ADDRESS_CITY' ,mapi_prop_tag(PT_TSTRING, 0x3A59)); -define('PR_HOME_ADDRESS_CITY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A59)); -define('PR_HOME_ADDRESS_CITY_A' ,mapi_prop_tag(PT_STRING8, 0x3A59)); - -define('PR_HOME_ADDRESS_COUNTRY' ,mapi_prop_tag(PT_TSTRING, 0x3A5A)); -define('PR_HOME_ADDRESS_COUNTRY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5A)); -define('PR_HOME_ADDRESS_COUNTRY_A' ,mapi_prop_tag(PT_STRING8, 0x3A5A)); - -define('PR_HOME_ADDRESS_POSTAL_CODE' ,mapi_prop_tag(PT_TSTRING, 0x3A5B)); -define('PR_HOME_ADDRESS_POSTAL_CODE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5B)); -define('PR_HOME_ADDRESS_POSTAL_CODE_A' ,mapi_prop_tag(PT_STRING8, 0x3A5B)); - -define('PR_HOME_ADDRESS_STATE_OR_PROVINCE' ,mapi_prop_tag(PT_TSTRING, 0x3A5C)); -define('PR_HOME_ADDRESS_STATE_OR_PROVINCE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5C)); -define('PR_HOME_ADDRESS_STATE_OR_PROVINCE_A' ,mapi_prop_tag(PT_STRING8, 0x3A5C)); - -define('PR_HOME_ADDRESS_STREET' ,mapi_prop_tag(PT_TSTRING, 0x3A5D)); -define('PR_HOME_ADDRESS_STREET_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5D)); -define('PR_HOME_ADDRESS_STREET_A' ,mapi_prop_tag(PT_STRING8, 0x3A5D)); - -define('PR_HOME_ADDRESS_POST_OFFICE_BOX' ,mapi_prop_tag(PT_TSTRING, 0x3A5E)); -define('PR_HOME_ADDRESS_POST_OFFICE_BOX_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5E)); -define('PR_HOME_ADDRESS_POST_OFFICE_BOX_A' ,mapi_prop_tag(PT_STRING8, 0x3A5E)); - -define('PR_OTHER_ADDRESS_CITY' ,mapi_prop_tag(PT_TSTRING, 0x3A5F)); -define('PR_OTHER_ADDRESS_CITY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A5F)); -define('PR_OTHER_ADDRESS_CITY_A' ,mapi_prop_tag(PT_STRING8, 0x3A5F)); - -define('PR_OTHER_ADDRESS_COUNTRY' ,mapi_prop_tag(PT_TSTRING, 0x3A60)); -define('PR_OTHER_ADDRESS_COUNTRY_W' ,mapi_prop_tag(PT_UNICODE, 0x3A60)); -define('PR_OTHER_ADDRESS_COUNTRY_A' ,mapi_prop_tag(PT_STRING8, 0x3A60)); - -define('PR_OTHER_ADDRESS_POSTAL_CODE' ,mapi_prop_tag(PT_TSTRING, 0x3A61)); -define('PR_OTHER_ADDRESS_POSTAL_CODE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A61)); -define('PR_OTHER_ADDRESS_POSTAL_CODE_A' ,mapi_prop_tag(PT_STRING8, 0x3A61)); - -define('PR_OTHER_ADDRESS_STATE_OR_PROVINCE' ,mapi_prop_tag(PT_TSTRING, 0x3A62)); -define('PR_OTHER_ADDRESS_STATE_OR_PROVINCE_W' ,mapi_prop_tag(PT_UNICODE, 0x3A62)); -define('PR_OTHER_ADDRESS_STATE_OR_PROVINCE_A' ,mapi_prop_tag(PT_STRING8, 0x3A62)); - -define('PR_OTHER_ADDRESS_STREET' ,mapi_prop_tag(PT_TSTRING, 0x3A63)); -define('PR_OTHER_ADDRESS_STREET_W' ,mapi_prop_tag(PT_UNICODE, 0x3A63)); -define('PR_OTHER_ADDRESS_STREET_A' ,mapi_prop_tag(PT_STRING8, 0x3A63)); - -define('PR_OTHER_ADDRESS_POST_OFFICE_BOX' ,mapi_prop_tag(PT_TSTRING, 0x3A64)); -define('PR_OTHER_ADDRESS_POST_OFFICE_BOX_W' ,mapi_prop_tag(PT_UNICODE, 0x3A64)); -define('PR_OTHER_ADDRESS_POST_OFFICE_BOX_A' ,mapi_prop_tag(PT_STRING8, 0x3A64)); - -define('PR_USER_X509_CERTIFICATE' ,mapi_prop_tag(PT_MV_BINARY, 0x3A70)); - -/* - * Profile section properties - */ - -define('PR_STORE_PROVIDERS' ,mapi_prop_tag(PT_BINARY, 0x3D00)); -define('PR_AB_PROVIDERS' ,mapi_prop_tag(PT_BINARY, 0x3D01)); -define('PR_TRANSPORT_PROVIDERS' ,mapi_prop_tag(PT_BINARY, 0x3D02)); - -define('PR_DEFAULT_PROFILE' ,mapi_prop_tag(PT_BOOLEAN, 0x3D04)); -define('PR_AB_SEARCH_PATH' ,mapi_prop_tag(PT_MV_BINARY, 0x3D05)); -define('PR_AB_DEFAULT_DIR' ,mapi_prop_tag(PT_BINARY, 0x3D06)); -define('PR_AB_DEFAULT_PAB' ,mapi_prop_tag(PT_BINARY, 0x3D07)); - -define('PR_FILTERING_HOOKS' ,mapi_prop_tag(PT_BINARY, 0x3D08)); -define('PR_SERVICE_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3D09)); -define('PR_SERVICE_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3D09)); -define('PR_SERVICE_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3D09)); -define('PR_SERVICE_DLL_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3D0A)); -define('PR_SERVICE_DLL_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3D0A)); -define('PR_SERVICE_DLL_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3D0A)); -define('PR_SERVICE_ENTRY_NAME' ,mapi_prop_tag(PT_STRING8, 0x3D0B)); -define('PR_SERVICE_UID' ,mapi_prop_tag(PT_BINARY, 0x3D0C)); -define('PR_SERVICE_EXTRA_UIDS' ,mapi_prop_tag(PT_BINARY, 0x3D0D)); -define('PR_SERVICES' ,mapi_prop_tag(PT_BINARY, 0x3D0E)); -define('PR_SERVICE_SUPPORT_FILES' ,mapi_prop_tag(PT_MV_TSTRING, 0x3D0F)); -define('PR_SERVICE_SUPPORT_FILES_W' ,mapi_prop_tag(PT_MV_UNICODE, 0x3D0F)); -define('PR_SERVICE_SUPPORT_FILES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x3D0F)); -define('PR_SERVICE_DELETE_FILES' ,mapi_prop_tag(PT_MV_TSTRING, 0x3D10)); -define('PR_SERVICE_DELETE_FILES_W' ,mapi_prop_tag(PT_MV_UNICODE, 0x3D10)); -define('PR_SERVICE_DELETE_FILES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x3D10)); -define('PR_AB_SEARCH_PATH_UPDATE' ,mapi_prop_tag(PT_BINARY, 0x3D11)); -define('PR_PROFILE_NAME' ,mapi_prop_tag(PT_TSTRING, 0x3D12)); -define('PR_PROFILE_NAME_A' ,mapi_prop_tag(PT_STRING8, 0x3D12)); -define('PR_PROFILE_NAME_W' ,mapi_prop_tag(PT_UNICODE, 0x3D12)); - -/* - * Status object properties - */ - -define('PR_IDENTITY_DISPLAY' ,mapi_prop_tag(PT_TSTRING, 0x3E00)); -define('PR_IDENTITY_DISPLAY_W' ,mapi_prop_tag(PT_UNICODE, 0x3E00)); -define('PR_IDENTITY_DISPLAY_A' ,mapi_prop_tag(PT_STRING8, 0x3E00)); -define('PR_IDENTITY_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x3E01)); -define('PR_RESOURCE_METHODS' ,mapi_prop_tag(PT_LONG, 0x3E02)); -define('PR_RESOURCE_TYPE' ,mapi_prop_tag(PT_LONG, 0x3E03)); -define('PR_STATUS_CODE' ,mapi_prop_tag(PT_LONG, 0x3E04)); -define('PR_IDENTITY_SEARCH_KEY' ,mapi_prop_tag(PT_BINARY, 0x3E05)); -define('PR_OWN_STORE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x3E06)); -define('PR_RESOURCE_PATH' ,mapi_prop_tag(PT_TSTRING, 0x3E07)); -define('PR_RESOURCE_PATH_W' ,mapi_prop_tag(PT_UNICODE, 0x3E07)); -define('PR_RESOURCE_PATH_A' ,mapi_prop_tag(PT_STRING8, 0x3E07)); -define('PR_STATUS_STRING' ,mapi_prop_tag(PT_TSTRING, 0x3E08)); -define('PR_STATUS_STRING_W' ,mapi_prop_tag(PT_UNICODE, 0x3E08)); -define('PR_STATUS_STRING_A' ,mapi_prop_tag(PT_STRING8, 0x3E08)); -define('PR_X400_DEFERRED_DELIVERY_CANCEL' ,mapi_prop_tag(PT_BOOLEAN, 0x3E09)); -define('PR_HEADER_FOLDER_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x3E0A)); -define('PR_REMOTE_PROGRESS' ,mapi_prop_tag(PT_LONG, 0x3E0B)); -define('PR_REMOTE_PROGRESS_TEXT' ,mapi_prop_tag(PT_TSTRING, 0x3E0C)); -define('PR_REMOTE_PROGRESS_TEXT_W' ,mapi_prop_tag(PT_UNICODE, 0x3E0C)); -define('PR_REMOTE_PROGRESS_TEXT_A' ,mapi_prop_tag(PT_STRING8, 0x3E0C)); -define('PR_REMOTE_VALIDATE_OK' ,mapi_prop_tag(PT_BOOLEAN, 0x3E0D)); - -/* - * Display table properties - */ - -define('PR_CONTROL_FLAGS' ,mapi_prop_tag(PT_LONG, 0x3F00)); -define('PR_CONTROL_STRUCTURE' ,mapi_prop_tag(PT_BINARY, 0x3F01)); -define('PR_CONTROL_TYPE' ,mapi_prop_tag(PT_LONG, 0x3F02)); -define('PR_DELTAX' ,mapi_prop_tag(PT_LONG, 0x3F03)); -define('PR_DELTAY' ,mapi_prop_tag(PT_LONG, 0x3F04)); -define('PR_XPOS' ,mapi_prop_tag(PT_LONG, 0x3F05)); -define('PR_YPOS' ,mapi_prop_tag(PT_LONG, 0x3F06)); -define('PR_CONTROL_ID' ,mapi_prop_tag(PT_BINARY, 0x3F07)); -define('PR_INITIAL_DETAILS_PANE' ,mapi_prop_tag(PT_LONG, 0x3F08)); - -/* - * Secure property id range - */ - -define('PROP_ID_SECURE_MIN' ,0x67F0); -define('PROP_ID_SECURE_MAX' ,0x67FF); - -/* - * Extra properties - */ - -define('PR_IPM_APPOINTMENT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D0)); -define('PR_IPM_CONTACT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D1)); -define('PR_IPM_JOURNAL_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D2)); -define('PR_IPM_NOTE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D3)); -define('PR_IPM_TASK_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D4)); -define('PR_IPM_DRAFTS_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D7)); -/* -PR_ADDITIONAL_REN_ENTRYIDS: - This is a multivalued property which contains entry IDs for certain special folders. - The first 5 (0-4) entries in this multivalued property are as follows: - 0 - Conflicts folder - 1 - Sync Issues folder - 2 - Local Failures folder - 3 - Server Failures folder - 4 - Junk E-mail Folder - 5 - sfSpamTagDontUse (unknown what this is, disable olk spam stuff?) -*/ -define('PR_ADDITIONAL_REN_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, 0x36D8)); -define('PR_FREEBUSY_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, 0x36E4)); -define('PR_REM_ONLINE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D5)); -define('PR_REM_OFFLINE_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x36D6)); -/* -PR_IPM_OL2007_ENTRYIDS: - This is a single binary property containing the entryids for: - - 'Rss feeds' folder - - The searchfolder 'Tracked mail processing' - - The searchfolder 'To-do list' - - However, it is encoded something like the following: - - 01803200 (type: rss feeds ?) - 0100 - 2E00 - 00000000B774162F0098C84182DE9E4358E4249D01000B41FF66083D464EA7E34D6026C9B143000000006DDA0000 (entryid) - 04803200 (type: tracked mail processing ?) - 0100 - 2E00 - 00000000B774162F0098C84182DE9E4358E4249D01000B41FF66083D464EA7E34D6026C9B143000000006DDB0000 (entryid) - 02803200 (type: todo list ?) - 0100 - 2E00 - 00000000B774162F0098C84182DE9E4358E4249D01000B41FF66083D464EA7E34D6026C9B143000000006DE40000 (entryid) - 00000000 (terminator?) - - It may also only contain the rss feeds entryid, and then have the 00000000 terminator directly after the entryid: - - 01803200 (type: rss feeds ?) - 0100 - 2E00 - 00000000B774162F0098C84182DE9E4358E4249D01000B41FF66083D464EA7E34D6026C9B143000000006DDA0000 (entryid) - 00000000 (terminator?) -*/ -define('PR_IPM_OL2007_ENTRYIDS' ,mapi_prop_tag(PT_BINARY, 0x36D9)); - - - -/* - * Don't know where to put these - */ - -define('PR_ICON_INDEX' ,mapi_prop_tag(PT_LONG, 0x1080)); -define('PR_LAST_VERB_EXECUTED' ,mapi_prop_tag(PT_LONG, 0x1081)); -define('PR_LAST_VERB_EXECUTION_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x1082)); -define('PR_INTERNET_CPID' ,mapi_prop_tag(PT_LONG, 0x3FDE)); -define('PR_RECIPIENT_ENTRYID' ,mapi_prop_tag(PT_BINARY, 0x5FF7)); -define('PR_SEND_INTERNET_ENCODING' ,mapi_prop_tag(PT_LONG, 0x3FDE)); -define('PR_RECIPIENT_DISPLAY_NAME' ,mapi_prop_tag(PT_STRING8, 0x5FF6)); -define('PR_RECIPIENT_TRACKSTATUS' ,mapi_prop_tag(PT_LONG, 0x5FFF)); -define('PR_RECIPIENT_FLAGS' ,mapi_prop_tag(PT_LONG, 0x5FFD)); -define('PR_RECIPIENT_TRACKSTATUS_TIME' ,mapi_prop_tag(PT_SYSTIME, 0x5FFB)); - -define('PR_EC_BASE' , 0x6700); -define('PR_EC_OUTOFOFFICE' ,mapi_prop_tag(PT_BOOLEAN, PR_EC_BASE+0x60)); -define('PR_EC_OUTOFOFFICE_MSG' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x61)); -define('PR_EC_OUTOFOFFICE_SUBJECT' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x62)); - -/* quota support */ -define('PR_QUOTA_WARNING_THRESHOLD' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x21)); -define('PR_QUOTA_SEND_THRESHOLD' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x22)); -define('PR_QUOTA_RECEIVE_THRESHOLD' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x23)); - -/* storage for the settings for the webaccess 6.xx */ -define('PR_EC_WEBACCESS_SETTINGS' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x70)); -define('PR_EC_RECIPIENT_HISTORY' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x71)); - -/* storage for the settings for the webaccess 7.xx */ -define('PR_EC_WEBACCESS_SETTINGS_JSON' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x72)); -define('PR_EC_RECIPIENT_HISTORY_JSON' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x73)); - -/* statistics properties */ -define('PR_EC_STATSTABLE_SYSTEM' ,mapi_prop_tag(PT_OBJECT, PR_EC_BASE+0x30)); -define('PR_EC_STATSTABLE_SESSIONS' ,mapi_prop_tag(PT_OBJECT, PR_EC_BASE+0x31)); -define('PR_EC_STATSTABLE_USERS' ,mapi_prop_tag(PT_OBJECT, PR_EC_BASE+0x32)); -define('PR_EC_STATSTABLE_COMPANY' ,mapi_prop_tag(PT_OBJECT, PR_EC_BASE+0x33)); - -define('PR_EC_STATS_SYSTEM_DESCRIPTION' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x40)); -define('PR_EC_STATS_SYSTEM_VALUE' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x41)); -define('PR_EC_STATS_SESSION_ID' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x42)); -define('PR_EC_STATS_SESSION_IPADDRESS' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x43)); -define('PR_EC_STATS_SESSION_IDLETIME' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x44)); -define('PR_EC_STATS_SESSION_CAPABILITY' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x45)); -define('PR_EC_STATS_SESSION_LOCKED' ,mapi_prop_tag(PT_BOOLEAN, PR_EC_BASE+0x46)); -define('PR_EC_STATS_SESSION_BUSYSTATES' ,mapi_prop_tag(PT_MV_STRING8, PR_EC_BASE+0x47)); -define('PR_EC_COMPANY_NAME' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x48)); - -/* user features */ -define('PR_EC_ENABLED_FEATURES' ,mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE+0xB3)); -define('PR_EC_ENABLED_FEATURES_A' ,mapi_prop_tag(PT_MV_STRING8, PR_EC_BASE+0xB3)); -define('PR_EC_ENABLED_FEATURES_W' ,mapi_prop_tag(PT_MV_UNICODE, PR_EC_BASE+0xB3)); - -define('PR_EC_DISABLED_FEATURES' ,mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE+0xB4)); -define('PR_EC_DISABLED_FEATURES_A' ,mapi_prop_tag(PT_MV_STRING8, PR_EC_BASE+0xB4)); -define('PR_EC_DISABLED_FEATURES_W' ,mapi_prop_tag(PT_MV_UNICODE, PR_EC_BASE+0xB4)); - -/* WA properties */ -define('PR_EC_WA_ATTACHMENT_HIDDEN_OVERRIDE' ,mapi_prop_tag(PT_BOOLEAN, PR_EC_BASE+0xE0)); - -// edkmdb, rules properties -#define pidSpecialMin 0x6670 -define('PR_RULE_ID' ,mapi_prop_tag(PT_I8, 0x6670+0x04)); // only lower 32bits are used. -define('PR_RULE_IDS' ,mapi_prop_tag(PT_BINARY, 0x6670+0x05)); -define('PR_RULE_SEQUENCE' ,mapi_prop_tag(PT_LONG, 0x6670+0x06)); -define('PR_RULE_STATE' ,mapi_prop_tag(PT_LONG, 0x6670+0x07)); -define('PR_RULE_USER_FLAGS' ,mapi_prop_tag(PT_LONG, 0x6670+0x08)); -define('PR_RULE_CONDITION' ,mapi_prop_tag(PT_SRESTRICTION,0x6670+0x09)); -define('PR_RULE_ACTIONS' ,mapi_prop_tag(PT_ACTIONS, 0x6670+0x10)); -define('PR_RULE_PROVIDER' ,mapi_prop_tag(PT_STRING8, 0x6670+0x11)); -define('PR_RULE_NAME' ,mapi_prop_tag(PT_TSTRING, 0x6670+0x12)); -define('PR_RULE_LEVEL' ,mapi_prop_tag(PT_LONG, 0x6670+0x13)); -define('PR_RULE_PROVIDER_DATA' ,mapi_prop_tag(PT_BINARY, 0x6670+0x14)); - -// edkmdb, ICS properties -define('PR_SOURCE_KEY' ,mapi_prop_tag(PT_BINARY, 0x65E0+0x00)); -define('PR_PARENT_SOURCE_KEY' ,mapi_prop_tag(PT_BINARY, 0x65E0+0x01)); -define('PR_CHANGE_KEY' ,mapi_prop_tag(PT_BINARY, 0x65E0+0x02)); -define('PR_PREDECESSOR_CHANGE_LIST' ,mapi_prop_tag(PT_BINARY, 0x65E0+0x03)); - - -define('PR_PROCESS_MEETING_REQUESTS' ,mapi_prop_tag(PT_BOOLEAN, 0x686D)); -define('PR_DECLINE_RECURRING_MEETING_REQUESTS' ,mapi_prop_tag(PT_BOOLEAN, 0x686E)); -define('PR_DECLINE_CONFLICTING_MEETING_REQUESTS' ,mapi_prop_tag(PT_BOOLEAN, 0x686F)); - - -define('PR_PROPOSEDNEWTIME' ,mapi_prop_tag(PT_BOOLEAN, 0x5FE1)); -define('PR_PROPOSENEWTIME_START' ,mapi_prop_tag(PT_SYSTIME, 0x5FE3)); -define('PR_PROPOSENEWTIME_END' ,mapi_prop_tag(PT_SYSTIME, 0x5FE4)); - -// property for sort the recoverable items. -define('PR_DELETED_ON' ,mapi_prop_tag(PT_SYSTIME, 0x668F)); - -define('PR_PROCESSED' ,mapi_prop_tag(PT_BOOLEAN, 0x7D01)); - -// Delegates properties -define('PR_DELEGATES_SEE_PRIVATE' ,mapi_prop_tag(PT_MV_LONG, 0x686B)); -define('PR_SCHDINFO_DELEGATE_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, 0x6845)); -define('PR_SCHDINFO_DELEGATE_NAMES' ,mapi_prop_tag(PT_MV_STRING8, 0x6844)); -define('PR_DELEGATED_BY_RULE' ,mapi_prop_tag(PT_BOOLEAN, 0x3FE3)); - -// properties required in Reply mail. -define('PR_INTERNET_REFERENCES' ,mapi_prop_tag(PT_STRING8, 0x1039)); -define('PR_IN_REPLY_TO_ID' ,mapi_prop_tag(PT_STRING8, 0x1042)); -define('PR_INTERNET_MESSAGE_ID' ,mapi_prop_tag(PT_STRING8, 0x1035)); - -// for hidden folders -define('PR_ATTR_HIDDEN' ,mapi_prop_tag(PT_BOOLEAN, 0x10F4)); - -/** - * Addressbook detail properties. - * It is not defined by MAPI, but to keep in sync with the interface of outlook we have to use these - * properties. Outlook actually uses these properties for it's addressbook details. - */ -define('PR_HOME2_TELEPHONE_NUMBER_MV' ,mapi_prop_tag(PT_MV_TSTRING, 0x3A2F)); -define('PR_BUSINESS2_TELEPHONE_NUMBER_MV' ,mapi_prop_tag(PT_MV_TSTRING, 0x3A1B)); -define('PR_EMS_AB_PROXY_ADDRESSES' ,mapi_prop_tag(PT_TSTRING, 0x800F)); -define('PR_EMS_AB_PROXY_ADDRESSES_MV' ,mapi_prop_tag(PT_MV_TSTRING, 0x800F)); -define('PR_EMS_AB_MANAGER' ,mapi_prop_tag(PT_BINARY, 0x8005)); -define('PR_EMS_AB_REPORTS' ,mapi_prop_tag(PT_BINARY, 0x800E)); -define('PR_EMS_AB_REPORTS_MV' ,mapi_prop_tag(PT_MV_BINARY, 0x800E)); -define('PR_EMS_AB_IS_MEMBER_OF_DL' ,mapi_prop_tag(PT_MV_BINARY, 0x8008)); -define('PR_EMS_AB_OWNER' ,mapi_prop_tag(PT_BINARY, 0x800C)); -define('PR_EMS_AB_ROOM_CAPACITY' ,mapi_prop_tag(PT_LONG, 0x0807)); -define('PR_EMS_AB_TAGGED_X509_CERT' ,mapi_prop_tag(PT_MV_BINARY, 0x8C6A)); - -define('PR_EC_ARCHIVE_SERVERS' ,mapi_prop_tag(PT_MV_TSTRING, 0x67c4)); - -/* zarafa contacts provider properties */ -define('PR_ZC_CONTACT_STORE_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE+0x11)); -define('PR_ZC_CONTACT_FOLDER_ENTRYIDS' ,mapi_prop_tag(PT_MV_BINARY, PR_EC_BASE+0x12)); -define('PR_ZC_CONTACT_FOLDER_NAMES' ,mapi_prop_tag(PT_MV_TSTRING, PR_EC_BASE+0x13)); - -//Properties defined for Z-Push -define('PR_TODO_ITEM_FLAGS' ,mapi_prop_tag(PT_LONG, 0x0E2B)); diff --git a/sources/backend/zarafa/mapimapping.php b/sources/backend/zarafa/mapimapping.php deleted file mode 100644 index 2d6795d..0000000 --- a/sources/backend/zarafa/mapimapping.php +++ /dev/null @@ -1,526 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ -/** - * - * MAPI to AS mapping class - * - * - */ -class MAPIMapping { - /** - * Returns the MAPI to AS mapping for contacts - * - * @return array - */ - public static function GetContactMapping() { - return array ( - "anniversary" => PR_WEDDING_ANNIVERSARY, - "assistantname" => PR_ASSISTANT, - "assistnamephonenumber" => PR_ASSISTANT_TELEPHONE_NUMBER, - "birthday" => PR_BIRTHDAY, - "body" => PR_BODY, - "business2phonenumber" => PR_BUSINESS2_TELEPHONE_NUMBER, - "businesscity" => "PT_STRING8:PSETID_Address:0x8046", - "businesscountry" => "PT_STRING8:PSETID_Address:0x8049", - "businesspostalcode" => "PT_STRING8:PSETID_Address:0x8048", - "businessstate" => "PT_STRING8:PSETID_Address:0x8047", - "businessstreet" => "PT_STRING8:PSETID_Address:0x8045", - "businessfaxnumber" => PR_BUSINESS_FAX_NUMBER, - "businessphonenumber" => PR_OFFICE_TELEPHONE_NUMBER, - "carphonenumber" => PR_CAR_TELEPHONE_NUMBER, - "categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords", - "children" => PR_CHILDRENS_NAMES, - "companyname" => PR_COMPANY_NAME, - "department" => PR_DEPARTMENT_NAME, - "email1address" => "PT_STRING8:PSETID_Address:0x8083", - "email2address" => "PT_STRING8:PSETID_Address:0x8093", - "email3address" => "PT_STRING8:PSETID_Address:0x80A3", - "fileas" => "PT_STRING8:PSETID_Address:0x8005", - "firstname" => PR_GIVEN_NAME, - "home2phonenumber" => PR_HOME2_TELEPHONE_NUMBER, - "homecity" => PR_HOME_ADDRESS_CITY, - "homecountry" => PR_HOME_ADDRESS_COUNTRY, - "homepostalcode" => PR_HOME_ADDRESS_POSTAL_CODE, - "homestate" => PR_HOME_ADDRESS_STATE_OR_PROVINCE, - "homestreet" => PR_HOME_ADDRESS_STREET, - "homefaxnumber" => PR_HOME_FAX_NUMBER, - "homephonenumber" => PR_HOME_TELEPHONE_NUMBER, - "jobtitle" => PR_TITLE, - "lastname" => PR_SURNAME, - "middlename" => PR_MIDDLE_NAME, - "mobilephonenumber" => PR_CELLULAR_TELEPHONE_NUMBER, - "officelocation" => PR_OFFICE_LOCATION, - "othercity" => PR_OTHER_ADDRESS_CITY, - "othercountry" => PR_OTHER_ADDRESS_COUNTRY, - "otherpostalcode" => PR_OTHER_ADDRESS_POSTAL_CODE, - "otherstate" => PR_OTHER_ADDRESS_STATE_OR_PROVINCE, - "otherstreet" => PR_OTHER_ADDRESS_STREET, - "pagernumber" => PR_PAGER_TELEPHONE_NUMBER, - "radiophonenumber" => PR_RADIO_TELEPHONE_NUMBER, - "spouse" => PR_SPOUSE_NAME, - "suffix" => PR_GENERATION, - "title" => PR_DISPLAY_NAME_PREFIX, - "webpage" => "PT_STRING8:PSETID_Address:0x802b", - "yomicompanyname" => "PT_STRING8:PSETID_Address:0x802e", - "yomifirstname" => "PT_STRING8:PSETID_Address:0x802c", - "yomilastname" => "PT_STRING8:PSETID_Address:0x802d", - "rtf" => PR_RTF_COMPRESSED, - // picture - "customerid" => PR_CUSTOMER_ID, - "governmentid" => PR_GOVERNMENT_ID_NUMBER, - "imaddress" => "PT_STRING8:PSETID_Address:0x8062", - "imaddress2" => "PT_STRING8:PSETID_AirSync:IMAddress2", - "imaddress3" => "PT_STRING8:PSETID_AirSync:IMAddress3", - "managername" => PR_MANAGER_NAME, - "companymainphone" => PR_COMPANY_MAIN_PHONE_NUMBER, - "accountname" => PR_ACCOUNT, - "nickname" => PR_NICKNAME, - // mms - ); - } - - - /** - * - * Returns contact specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetContactProperties() { - return array ( - "haspic" => "PT_BOOLEAN:PSETID_Address:0x8015", - "emailaddress1" => "PT_STRING8:PSETID_Address:0x8083", - "emailaddressdname1" => "PT_STRING8:PSETID_Address:0x8080", - "emailaddressdemail1" => "PT_STRING8:PSETID_Address:0x8084", - "emailaddresstype1" => "PT_STRING8:PSETID_Address:0x8082", - "emailaddressentryid1" => "PT_BINARY:PSETID_Address:0x8085", - "emailaddress2" => "PT_STRING8:PSETID_Address:0x8093", - "emailaddressdname2" => "PT_STRING8:PSETID_Address:0x8090", - "emailaddressdemail2" => "PT_STRING8:PSETID_Address:0x8094", - "emailaddresstype2" => "PT_STRING8:PSETID_Address:0x8092", - "emailaddressentryid2" => "PT_BINARY:PSETID_Address:0x8095", - "emailaddress3" => "PT_STRING8:PSETID_Address:0x80a3", - "emailaddressdname3" => "PT_STRING8:PSETID_Address:0x80a0", - "emailaddressdemail3" => "PT_STRING8:PSETID_Address:0x80a4", - "emailaddresstype3" => "PT_STRING8:PSETID_Address:0x80a2", - "emailaddressentryid3" => "PT_BINARY:PSETID_Address:0x80a5", - "addressbookmv" => "PT_MV_LONG:PSETID_Address:0x8028", - "addressbooklong" => "PT_LONG:PSETID_Address:0x8029", - "displayname" => PR_DISPLAY_NAME, - "subject" => PR_SUBJECT, - "country" => PR_COUNTRY, - "city" => PR_LOCALITY, - "postaladdress" => PR_POSTAL_ADDRESS, - "postalcode" => PR_POSTAL_CODE, - "state" => PR_STATE_OR_PROVINCE, - "street" => PR_STREET_ADDRESS, - "homeaddress" => "PT_STRING8:PSETID_Address:0x801a", - "businessaddress" => "PT_STRING8:PSETID_Address:0x801b", - "otheraddress" => "PT_STRING8:PSETID_Address:0x801c", - "mailingaddress" => "PT_LONG:PSETID_Address:0x8022", - ); - } - - - /** - * Returns the MAPI to AS mapping for emails - * - * @return array - */ - public static function GetEmailMapping() { - return array ( - // from - "datereceived" => PR_MESSAGE_DELIVERY_TIME, - "displayname" => PR_SUBJECT, - "displayto" => PR_DISPLAY_TO, - "importance" => PR_IMPORTANCE, - "messageclass" => PR_MESSAGE_CLASS, - "subject" => PR_SUBJECT, - "read" => PR_MESSAGE_FLAGS, - // "to" // need to be generated with SMTP addresses - // "cc" - // "threadtopic" => PR_CONVERSATION_TOPIC, - "internetcpid" => PR_INTERNET_CPID, - "nativebodytype" => PR_NATIVE_BODY_INFO, - "lastverbexecuted" => PR_LAST_VERB_EXECUTED, - "lastverbexectime" => PR_LAST_VERB_EXECUTION_TIME, - ); - } - - - /** - * - * Returns email specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetEmailProperties() { - return array ( - // Override 'From' to show "Full Name " - "representingname" => PR_SENT_REPRESENTING_NAME, - "representingentryid" => PR_SENT_REPRESENTING_ENTRYID, - "sourcekey" => PR_SOURCE_KEY, - "entryid" => PR_ENTRYID, - "body" => PR_BODY, - "rtfcompressed" => PR_RTF_COMPRESSED, - "html" => PR_HTML, - "rtfinsync" => PR_RTF_IN_SYNC, - "processed" => PR_PROCESSED, - ); - } - - - /** - * Returns the MAPI to AS mapping for meeting requests - * - * @return array - */ - public static function GetMeetingRequestMapping() { - return array ( - "responserequested" => PR_RESPONSE_REQUESTED, - // timezone - "alldayevent" => "PT_BOOLEAN:PSETID_Appointment:0x8215", - "busystatus" => "PT_LONG:PSETID_Appointment:0x8224", - "rtf" => PR_RTF_COMPRESSED, - "dtstamp" => PR_LAST_MODIFICATION_TIME, - "endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e", - "location" => "PT_STRING8:PSETID_Appointment:0x8208", - // recurrences - "reminder" => "PT_LONG:PSETID_Common:0x8501", - "starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d", - "sensitivity" => PR_SENSITIVITY, - ); - } - - - public static function GetMeetingRequestProperties() { - return array ( - "goidtag" => "PT_BINARY:PSETID_Meeting:0x3", - "timezonetag" => "PT_BINARY:PSETID_Appointment:0x8233", - "recReplTime" => "PT_SYSTIME:PSETID_Appointment:0x8228", - "isrecurringtag" => "PT_BOOLEAN:PSETID_Appointment:0x8223", - "recurringstate" => "PT_BINARY:PSETID_Appointment:0x8216", - "appSeqNr" => "PT_LONG:PSETID_Appointment:0x8201", - "lidIsException" => "PT_BOOLEAN:PSETID_Appointment:0xA", - "recurStartTime" => "PT_LONG:PSETID_Meeting:0xE", - "reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503", - "remindertime" => "PT_LONG:PSETID_Common:0x8501", - "recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236", - ); - } - - - public static function GetTnefAndIcalProperties() { - return array( - "starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d", - "endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e", - "commonstart" => "PT_SYSTIME:PSETID_Common:0x8516", - "commonend" => "PT_SYSTIME:PSETID_Common:0x8517", - "clipstart" => "PT_SYSTIME:PSETID_Appointment:0x8235", //ical only - "recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236", //ical only - "isrecurringtag" => "PT_BOOLEAN:PSETID_Appointment:0x8223", - "goidtag" => "PT_BINARY:PSETID_Meeting:0x3", - "goid2tag" => "PT_BINARY:PSETID_Meeting:0x23", - "usetnef" => "PT_LONG:PSETID_Meeting:0x8582", - "tneflocation" => "PT_STRING8:PSETID_Meeting:0x2", //ical only - "location" => "PT_STRING8:PSETID_Appointment:0x8208", - "tnefrecurr" => "PT_BOOLEAN:PSETID_Meeting:0x5", - "sideeffects" => "PT_LONG:PSETID_Common:0x8510", - "type" => "PT_STRING8:PSETID_Meeting:0x24", - "busystatus" => "PT_LONG:PSETID_Appointment:0x8205", - "meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217", - "responsestatus" => "PT_LONG:PSETID_Meeting:0x8218", - //the properties below are currently not used - "dayinterval" => "PT_I2:PSETID_Meeting:0x11", - "weekinterval" => "PT_I2:PSETID_Meeting:0x12", - "monthinterval" => "PT_I2:PSETID_Meeting:0x13", - "yearinterval" => "PT_I2:PSETID_Meeting:0x14", - ); - } - - - /** - * Returns the MAPI to AS mapping for appointments - * - * @return array - */ - public static function GetAppointmentMapping() { - return array ( - "alldayevent" => "PT_BOOLEAN:PSETID_Appointment:0x8215", - "body" => PR_BODY, - "busystatus" => "PT_LONG:PSETID_Appointment:0x8205", - "categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords", - "rtf" => PR_RTF_COMPRESSED, - "dtstamp" => PR_LAST_MODIFICATION_TIME, - "endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e", - "location" => "PT_STRING8:PSETID_Appointment:0x8208", - "meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217", - "sensitivity" => PR_SENSITIVITY, - "subject" => PR_SUBJECT, - "starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d", - "uid" => "PT_BINARY:PSETID_Meeting:0x3", - "nativebodytype" => PR_NATIVE_BODY_INFO, - ); - } - - - /** - * - * Returns appointment specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetAppointmentProperties() { - return array( - "sourcekey" => PR_SOURCE_KEY, - "representingentryid" => PR_SENT_REPRESENTING_ENTRYID, - "representingname" => PR_SENT_REPRESENTING_NAME, - "sentrepresentingemail" => PR_SENT_REPRESENTING_EMAIL_ADDRESS, - "sentrepresentingaddt" => PR_SENT_REPRESENTING_ADDRTYPE, - "sentrepresentinsrchk" => PR_SENT_REPRESENTING_SEARCH_KEY, - "reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503", - "remindertime" => "PT_LONG:PSETID_Common:0x8501", - "meetingstatus" => "PT_LONG:PSETID_Appointment:0x8217", - "isrecurring" => "PT_BOOLEAN:PSETID_Appointment:0x8223", - "recurringstate" => "PT_BINARY:PSETID_Appointment:0x8216", - "timezonetag" => "PT_BINARY:PSETID_Appointment:0x8233", - "recurrenceend" => "PT_SYSTIME:PSETID_Appointment:0x8236", - "responsestatus" => "PT_LONG:PSETID_Appointment:0x8218", - "commonstart" => "PT_SYSTIME:PSETID_Common:0x8516", - "commonend" => "PT_SYSTIME:PSETID_Common:0x8517", - "reminderstart" => "PT_SYSTIME:PSETID_Common:0x8502", - "duration" => "PT_LONG:PSETID_Appointment:0x8213", - "private" => "PT_BOOLEAN:PSETID_Common:0x8506", - "uid" => "PT_BINARY:PSETID_Meeting:0x23", - "sideeffects" => "PT_LONG:PSETID_Common:0x8510", - "flagdueby" => "PT_SYSTIME:PSETID_Common:0x8560", - "icon" => PR_ICON_INDEX, - "mrwassent" => "PT_BOOLEAN:PSETID_Appointment:0x8229", - "endtime" => "PT_SYSTIME:PSETID_Appointment:0x820e",//this is here for calendar restriction, tnef and ical - "starttime" => "PT_SYSTIME:PSETID_Appointment:0x820d",//this is here for calendar restriction, tnef and ical - "clipstart" => "PT_SYSTIME:PSETID_Appointment:0x8235", //ical only - "recurrencetype" => "PT_LONG:PSETID_Appointment:0x8231", - "body" => PR_BODY, - "rtfcompressed" => PR_RTF_COMPRESSED, - "html" => PR_HTML, - "rtfinsync" => PR_RTF_IN_SYNC, - ); - } - - - /** - * Returns the MAPI to AS mapping for tasks - * - * @return array - */ - public static function GetTaskMapping() { - return array ( - "body" => PR_BODY, - "categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords", - "complete" => "PT_BOOLEAN:PSETID_Task:0x811C", - "datecompleted" => "PT_SYSTIME:PSETID_Task:0x810F", - "duedate" => "PT_SYSTIME:PSETID_Task:0x8105", - "utcduedate" => "PT_SYSTIME:PSETID_Common:0x8517", - "utcstartdate" => "PT_SYSTIME:PSETID_Common:0x8516", - "importance" => PR_IMPORTANCE, - // recurrence - // regenerate - // deadoccur - "reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503", - "remindertime" => "PT_SYSTIME:PSETID_Common:0x8502", - "sensitivity" => PR_SENSITIVITY, - "startdate" => "PT_SYSTIME:PSETID_Task:0x8104", - "subject" => PR_SUBJECT, - "rtf" => PR_RTF_COMPRESSED, - "html" => PR_HTML, - ); - } - - - /** - * Returns task specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetTaskProperties() { - return array ( - "isrecurringtag" => "PT_BOOLEAN:PSETID_Task:0x8126", - "recurringstate" => "PT_BINARY:PSETID_Task:0x8116", - "deadoccur" => "PT_BOOLEAN:PSETID_Task:0x8109", - "completion" => "PT_DOUBLE:PSETID_Task:0x8102", - "status" => "PT_LONG:PSETID_Task:0x8101", - "icon" => PR_ICON_INDEX, - "owner" => "PT_STRING8:PSETID_Task:0x811F", - ); - } - - - /** - * Returns the MAPI to AS mapping for email todo flags - * - * @return array - */ - public static function GetMailFlagsMapping() { - return array ( - "flagstatus" => PR_FLAG_STATUS, - "flagtype" => "PT_STRING8:PSETID_Common:0x8530", - "datecompleted" => "PT_SYSTIME:PSETID_Common:0x810F", - "completetime" => PR_FLAG_COMPLETE_TIME, - "startdate" => "PT_SYSTIME:PSETID_Task:0x8104", - "duedate" => "PT_SYSTIME:PSETID_Task:0x8105", - "utcstartdate" => "PT_SYSTIME:PSETID_Common:0x8516", - "utcduedate" => "PT_SYSTIME:PSETID_Common:0x8517", - "reminderset" => "PT_BOOLEAN:PSETID_Common:0x8503", - "remindertime" => "PT_SYSTIME:PSETID_Common:0x8502", - "ordinaldate" => "PT_SYSTIME:PSETID_Common:0x85A0", - "subordinaldate" => "PT_STRING8:PSETID_Common:0x85A1", - - ); - } - - - /** - * Returns email todo flags' specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetMailFlagsProperties() { - return array( - "todoitemsflags" => PR_TODO_ITEM_FLAGS, - "todotitle" => "PT_STRING8:PSETID_Common:0x85A4", - "flagicon" => PR_FLAG_ICON, - "replyrequested" => PR_REPLY_REQUESTED, - "responserequested" => PR_RESPONSE_REQUESTED, - "status" => "PT_LONG:PSETID_Task:0x8101", - "completion" => "PT_DOUBLE:PSETID_Task:0x8102", - "complete" => "PT_BOOLEAN:PSETID_Task:0x811C", - ); - } - - - /** - * Returns the MAPI to AS mapping for notes - * - * @access public - * - * @return array - */ - public static function GetNoteMapping() { - return array( - "categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords", - "lastmodificationtime" => PR_LAST_MODIFICATION_TIME, - "messageclass" => PR_MESSAGE_CLASS, - "subject" => PR_SUBJECT, - ); - } - - - /** - * Returns note specific MAPI properties - * - * @access public - * - * @return array - */ - public static function GetNoteProperties() { - return array( - "body" => PR_BODY, - "messageclass" => PR_MESSAGE_CLASS, - "html" => PR_HTML, - "internetcpid" => PR_INTERNET_CPID, - - ); - } - - - /** - * Returns properties for sending an email - * - * @access public - * - * @return array - */ - public static function GetSendMailProperties() { - return array( - "outboxentryid" => PR_IPM_OUTBOX_ENTRYID, - "ipmsentmailentryid" => PR_IPM_SENTMAIL_ENTRYID, - "sentmailentryid" => PR_SENTMAIL_ENTRYID, - "subject" => PR_SUBJECT, - "messageclass" => PR_MESSAGE_CLASS, - "deliverytime" => PR_MESSAGE_DELIVERY_TIME, - "importance" => PR_IMPORTANCE, - "priority" => PR_PRIORITY, - "addrtype" => PR_ADDRTYPE, - "emailaddress" => PR_EMAIL_ADDRESS, - "displayname" => PR_DISPLAY_NAME, - "recipienttype" => PR_RECIPIENT_TYPE, - "entryid" => PR_ENTRYID, - "iconindex" => PR_ICON_INDEX, - "body" => PR_BODY, - "html" => PR_HTML, - "sentrepresentingname" => PR_SENT_REPRESENTING_NAME, - "sentrepresentingemail" => PR_SENT_REPRESENTING_EMAIL_ADDRESS, - "representingentryid" => PR_SENT_REPRESENTING_ENTRYID, - "sentrepresentingaddt" => PR_SENT_REPRESENTING_ADDRTYPE, - "sentrepresentinsrchk" => PR_SENT_REPRESENTING_SEARCH_KEY, - "displayto" => PR_DISPLAY_TO, - "displaycc" => PR_DISPLAY_CC, - "clientsubmittime" => PR_CLIENT_SUBMIT_TIME, - "attachnum" => PR_ATTACH_NUM, - "attachdatabin" => PR_ATTACH_DATA_BIN, - "internetcpid" => PR_INTERNET_CPID, - "rtf" => PR_RTF_COMPRESSED, - "rtfinsync" => PR_RTF_IN_SYNC, - ); - } -} \ No newline at end of file diff --git a/sources/backend/zarafa/mapiphpwrapper.php b/sources/backend/zarafa/mapiphpwrapper.php deleted file mode 100644 index 10cadf9..0000000 --- a/sources/backend/zarafa/mapiphpwrapper.php +++ /dev/null @@ -1,234 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -/** - * This is the PHP wrapper which strips MAPI information from - * the import interface of ICS. We get all the information about messages - * from MAPI here which are sent to the next importer, which will - * convert the data into WBXML which is streamed to the PDA - */ - -class PHPWrapper { - private $importer; - private $mapiprovider; - private $store; - private $contentparameters; - private $folderid; - - - /** - * Constructor of the PHPWrapper - * - * @param ressource $session - * @param ressource $store - * @param IImportChanges $importer incoming changes from ICS are forwarded here. - * @param string $folderid the folder this wrapper was configured for. - * - * @access public - * @return - */ - public function PHPWrapper($session, $store, $importer, $folderid) { - $this->importer = &$importer; - $this->store = $store; - $this->mapiprovider = new MAPIProvider($session, $this->store); - $this->folderid = $folderid; - } - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters) { - $this->contentparameters = $contentparameters; - } - - /** - * Implement MAPI interface - */ - public function Config($stream, $flags = 0) {} - public function GetLastError($hresult, $ulflags, &$lpmapierror) {} - public function UpdateState($stream) { } - - /** - * Imports a single message - * - * @param array $props - * @param long $flags - * @param object $retmapimessage - * - * @access public - * @return long - */ - public function ImportMessageChange($props, $flags, &$retmapimessage) { - $sourcekey = $props[PR_SOURCE_KEY]; - $parentsourcekey = $props[PR_PARENT_SOURCE_KEY]; - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, $parentsourcekey, $sourcekey); - - if(!$entryid) - return SYNC_E_IGNORE; - - $mapimessage = mapi_msgstore_openentry($this->store, $entryid); - try { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("PHPWrapper->ImportMessageChange(): Getting message from MAPIProvider, sourcekey: '%s', parentsourcekey: '%s', entryid: '%s'", bin2hex($sourcekey), bin2hex($parentsourcekey), bin2hex($entryid))); - $message = $this->mapiprovider->GetMessage($mapimessage, $this->contentparameters); - } - catch (SyncObjectBrokenException $mbe) { - $brokenSO = $mbe->GetSyncObject(); - if (!$brokenSO) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but broken SyncObject available")); - } - else { - if (!isset($brokenSO->id)) { - $brokenSO->id = "Unknown ID"; - ZLog::Write(LOGLEVEL_ERROR, sprintf("PHPWrapper->ImportMessageChange(): Catched SyncObjectBrokenException but no ID of object set")); - } - ZPush::GetDeviceManager()->AnnounceIgnoredMessage(false, $brokenSO->id, $brokenSO); - } - // tell MAPI to ignore the message - return SYNC_E_IGNORE; - } - - - // substitute the MAPI SYNC_NEW_MESSAGE flag by a z-push proprietary flag - if ($flags == SYNC_NEW_MESSAGE) $message->flags = SYNC_NEWMESSAGE; - else $message->flags = $flags; - - $this->importer->ImportMessageChange(bin2hex($sourcekey), $message); - - // Tell MAPI it doesn't need to do anything itself, as we've done all the work already. - return SYNC_E_IGNORE; - } - - /** - * Imports a list of messages to be deleted - * - * @param long $flags - * @param array $sourcekeys array with sourcekeys - * - * @access public - * @return - */ - public function ImportMessageDeletion($flags, $sourcekeys) { - $amount = count($sourcekeys); - if ($amount > 1000) { - throw new StatusException(sprintf("PHPWrapper->ImportMessageDeletion(): Received %d remove requests from ICS for folder '%s' (max. 1000 allowed). Triggering folder re-sync.", $amount, bin2hex($this->folderid)), SYNC_STATUS_INVALIDSYNCKEY, null, LOGLEVEL_ERROR); - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("PHPWrapper->ImportMessageDeletion(): Received %d remove requests from ICS", $amount)); - } - foreach($sourcekeys as $sourcekey) { - $this->importer->ImportMessageDeletion(bin2hex($sourcekey)); - } - } - - /** - * Imports a list of messages to be deleted - * - * @param mixed $readstates sourcekeys and message flags - * - * @access public - * @return - */ - public function ImportPerUserReadStateChange($readstates) { - foreach($readstates as $readstate) { - $this->importer->ImportMessageReadFlag(bin2hex($readstate["sourcekey"]), $readstate["flags"] & MSGFLAG_READ); - } - } - - /** - * Imports a message move - * this is never called by ICS - * - * @access public - * @return - */ - public function ImportMessageMove($sourcekeysrcfolder, $sourcekeysrcmessage, $message, $sourcekeydestmessage, $changenumdestmessage) { - // Never called - } - - /** - * Imports a single folder change - * - * @param array $props properties of the changed folder - * - * @access public - * @return - */ - function ImportFolderChange($props) { - $folder = $this->mapiprovider->GetFolder($props); - - // do not import folder if there is something "wrong" with it - if ($folder === false) - return 0; - - $this->importer->ImportFolderChange($folder); - return 0; - } - - /** - * Imports a list of folders which are to be deleted - * - * @param long $flags - * @param mixed $sourcekeys array with sourcekeys - * - * @access public - * @return - */ - function ImportFolderDeletion($flags, $sourcekeys) { - foreach ($sourcekeys as $sourcekey) { - $this->importer->ImportFolderDeletion(bin2hex($sourcekey)); - } - return 0; - } -} \ No newline at end of file diff --git a/sources/backend/zarafa/mapiprovider.php b/sources/backend/zarafa/mapiprovider.php deleted file mode 100644 index afec67f..0000000 --- a/sources/backend/zarafa/mapiprovider.php +++ /dev/null @@ -1,2682 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class MAPIProvider { - private $session; - private $store; - private $zRFC822; - private $addressbook; - private $storeProps; - private $inboxProps; - - /** - * Constructor of the MAPI Provider - * Almost all methods of this class require a MAPI session and/or store - * - * @param ressource $session - * @param ressource $store - * - * @access public - */ - function MAPIProvider($session, $store) { - $this->session = $session; - $this->store = $store; - } - - - /**---------------------------------------------------------------------------------------------------------- - * GETTER - */ - - /** - * Reads a message from MAPI - * Depending on the message class, a contact, appointment, task or email is read - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access public - * @return SyncObject - */ - public function GetMessage($mapimessage, $contentparameters) { - // Gets the Sync object from a MAPI object according to its message class - - $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS)); - if(isset($props[PR_MESSAGE_CLASS])) - $messageclass = $props[PR_MESSAGE_CLASS]; - else - $messageclass = "IPM"; - - if(strpos($messageclass,"IPM.Contact") === 0) - return $this->getContact($mapimessage, $contentparameters); - else if(strpos($messageclass,"IPM.Appointment") === 0) - return $this->getAppointment($mapimessage, $contentparameters); - else if(strpos($messageclass,"IPM.Task") === 0) - return $this->getTask($mapimessage, $contentparameters); - else if(strpos($messageclass,"IPM.StickyNote") === 0) - return $this->getNote($mapimessage, $contentparameters); - else - return $this->getEmail($mapimessage, $contentparameters); - } - - /** - * Reads a contact object from MAPI - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access private - * @return SyncContact - */ - private function getContact($mapimessage, $contentparameters) { - $message = new SyncContact(); - - // Standard one-to-one mappings first - $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetContactMapping()); - - // Contact specific props - $contactproperties = MAPIMapping::GetContactProperties(); - $messageprops = $this->getProps($mapimessage, $contactproperties); - - //set the body according to contentparameters and supported AS version - $this->setMessageBody($mapimessage, $contentparameters, $message); - - //check the picture - if (isset($messageprops[$contactproperties["haspic"]]) && $messageprops[$contactproperties["haspic"]]) { - // Add attachments - $attachtable = mapi_message_getattachmenttable($mapimessage); - mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction()); - $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE)); - - foreach($rows as $row) { - if(isset($row[PR_ATTACH_NUM])) { - $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]); - $message->picture = base64_encode(mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN)); - } - } - } - - return $message; - } - - /** - * Reads a task object from MAPI - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access private - * @return SyncTask - */ - private function getTask($mapimessage, $contentparameters) { - $message = new SyncTask(); - - // Standard one-to-one mappings first - $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetTaskMapping()); - - // Task specific props - $taskproperties = MAPIMapping::GetTaskProperties(); - $messageprops = $this->getProps($mapimessage, $taskproperties); - - //set the body according to contentparameters and supported AS version - $this->setMessageBody($mapimessage, $contentparameters, $message); - - //task with deadoccur is an occurrence of a recurring task and does not need to be handled as recurring - //webaccess does not set deadoccur for the initial recurring task - if(isset($messageprops[$taskproperties["isrecurringtag"]]) && - $messageprops[$taskproperties["isrecurringtag"]] && - (!isset($messageprops[$taskproperties["deadoccur"]]) || - (isset($messageprops[$taskproperties["deadoccur"]]) && - !$messageprops[$taskproperties["deadoccur"]]))) { - // Process recurrence - $message->recurrence = new SyncTaskRecurrence(); - $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, false); - } - - // when set the task to complete using the WebAccess, the dateComplete property is not set correctly - if ($message->complete == 1 && !isset($message->datecompleted)) - $message->datecompleted = time(); - - // if no reminder is set, announce that to the mobile - if (!isset($message->reminderset)) - $message->reminderset = 0; - - return $message; - } - - /** - * Reads an appointment object from MAPI - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access private - * @return SyncAppointment - */ - private function getAppointment($mapimessage, $contentparameters) { - $message = new SyncAppointment(); - - // Standard one-to-one mappings first - $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetAppointmentMapping()); - - // Appointment specific props - $appointmentprops = MAPIMapping::GetAppointmentProperties(); - $messageprops = $this->getProps($mapimessage, $appointmentprops); - - //set the body according to contentparameters and supported AS version - $this->setMessageBody($mapimessage, $contentparameters, $message); - - // Set reminder time if reminderset is true - if(isset($messageprops[$appointmentprops["reminderset"]]) && $messageprops[$appointmentprops["reminderset"]] == true) { - if ($messageprops[$appointmentprops["remindertime"]] == 0x5AE980E1) - $message->reminder = 15; - else - $message->reminder = $messageprops[$appointmentprops["remindertime"]]; - } - - if(!isset($message->uid)) - $message->uid = bin2hex($messageprops[$appointmentprops["sourcekey"]]); - else - $message->uid = Utils::GetICalUidFromOLUid($message->uid); - - // Always set organizer information because some devices do not work properly without it - if( isset($messageprops[$appointmentprops["representingentryid"]]) && - isset($messageprops[$appointmentprops["representingname"]])) { - - $message->organizeremail = w2u($this->getSMTPAddressFromEntryID($messageprops[$appointmentprops["representingentryid"]])); - $message->organizername = w2u($messageprops[$appointmentprops["representingname"]]); - } - - if(isset($messageprops[$appointmentprops["timezonetag"]])) - $tz = $this->getTZFromMAPIBlob($messageprops[$appointmentprops["timezonetag"]]); - else { - // set server default timezone (correct timezone should be configured!) - $tz = TimezoneUtil::GetFullTZ(); - } - $message->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz)); - - if(isset($messageprops[$appointmentprops["isrecurring"]]) && $messageprops[$appointmentprops["isrecurring"]]) { - // Process recurrence - $message->recurrence = new SyncRecurrence(); - $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, $tz); - } - - // Do attendees - $reciptable = mapi_message_getrecipienttable($mapimessage); - // Only get first 256 recipients, to prevent possible load issues. - $rows = mapi_table_queryrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TYPE), 0, 256); - - // Exception: we do not synchronize appointments with more than 250 attendees - if (count($rows) > 250) { - $message->id = bin2hex($messageprops[$appointmentprops["sourcekey"]]); - $mbe = new SyncObjectBrokenException("Appointment has too many attendees"); - $mbe->SetSyncObject($message); - throw $mbe; - } - - if(count($rows) > 0) - $message->attendees = array(); - - foreach($rows as $row) { - $attendee = new SyncAttendee(); - - $attendee->name = w2u($row[PR_DISPLAY_NAME]); - //smtp address is always a proper email address - if(isset($row[PR_SMTP_ADDRESS])) - $attendee->email = w2u($row[PR_SMTP_ADDRESS]); - elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) { - //if address type is SMTP, it's also a proper email address - if ($row[PR_ADDRTYPE] == "SMTP") - $attendee->email = w2u($row[PR_EMAIL_ADDRESS]); - //if address type is ZARAFA, the PR_EMAIL_ADDRESS contains username - elseif ($row[PR_ADDRTYPE] == "ZARAFA") { - $userinfo = @mapi_zarafa_getuser_by_name($this->store, $row[PR_EMAIL_ADDRESS]); - if (is_array($userinfo) && isset($userinfo["emailaddress"])) { - $attendee->email = w2u($userinfo["emailaddress"]); - } - else - 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())); - } - } - - //set attendee's status and type if they're available and if we are the organizer - $storeprops = $this->getStoreProps(); - if (isset($row[PR_RECIPIENT_TRACKSTATUS]) && $messageprops[$appointmentprops["representingentryid"]] == $storeprops[PR_MAILBOX_OWNER_ENTRYID]) - $attendee->attendeestatus = $row[PR_RECIPIENT_TRACKSTATUS]; - if (isset($row[PR_RECIPIENT_TYPE])) - $attendee->attendeetype = $row[PR_RECIPIENT_TYPE]; - // Some attendees have no email or name (eg resources), and if you - // don't send one of those fields, the phone will give an error ... so - // we don't send it in that case. - // also ignore the "attendee" if the email is equal to the organizers' email - if(isset($attendee->name) && isset($attendee->email) && $attendee->email != "" && (!isset($message->organizeremail) || (isset($message->organizeremail) && $attendee->email != $message->organizeremail))) - array_push($message->attendees, $attendee); - } - - // Status 0 = no meeting, status 1 = organizer, status 2/3/4/5 = tentative/accepted/declined/notresponded - if(isset($messageprops[$appointmentprops["meetingstatus"]]) && $messageprops[$appointmentprops["meetingstatus"]] > 1) { - if (!isset($message->attendees) || !is_array($message->attendees)) - $message->attendees = array(); - // Work around iOS6 cancellation issue when there are no attendees for this meeting. Just add ourselves as the sole attendee. - if(count($message->attendees) == 0) { - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->getAppointment: adding ourself as an attendee for iOS6 workaround")); - $attendee = new SyncAttendee(); - - $meinfo = mapi_zarafa_getuser_by_name($this->store, Request::GetAuthUser()); - - if (is_array($meinfo)) { - $attendee->email = w2u($meinfo["emailaddress"]); - $attendee->name = w2u($meinfo["fullname"]); - $attendee->attendeetype = MAPI_TO; - - array_push($message->attendees, $attendee); - } - } - } - - if (!isset($message->nativebodytype)) $message->nativebodytype = $this->getNativeBodyType($messageprops); - - // If the user is working from a location other than the office the busystatus should be interpreted as free. - if (isset($message->busystatus) && $message->busystatus == fbWorkingElsewhere) { - $message->busystatus = fbFree; - } - - // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581 - if (isset($message->busystatus) && $message->busystatus == -1) { - $message->busystatus = fbTentative; - } - - return $message; - } - - /** - * Reads recurrence information from MAPI - * - * @param mixed $mapimessage - * @param array $recurprops - * @param SyncObject &$syncMessage the message - * @param SyncObject &$syncRecurrence the recurrence message - * @param array $tz timezone information - * - * @access private - * @return - */ - private function getRecurrence($mapimessage, $recurprops, &$syncMessage, &$syncRecurrence, $tz) { - if ($syncRecurrence instanceof SyncTaskRecurrence) - $recurrence = new TaskRecurrence($this->store, $mapimessage); - else - $recurrence = new Recurrence($this->store, $mapimessage); - - switch($recurrence->recur["type"]) { - case 10: // daily - switch($recurrence->recur["subtype"]) { - default: - $syncRecurrence->type = 0; - break; - case 1: - $syncRecurrence->type = 0; - $syncRecurrence->dayofweek = 62; // mon-fri - $syncRecurrence->interval = 1; - break; - } - break; - case 11: // weekly - $syncRecurrence->type = 1; - break; - case 12: // monthly - switch($recurrence->recur["subtype"]) { - default: - $syncRecurrence->type = 2; - break; - case 3: - $syncRecurrence->type = 3; - break; - } - break; - case 13: // yearly - switch($recurrence->recur["subtype"]) { - default: - $syncRecurrence->type = 4; - break; - case 2: - $syncRecurrence->type = 5; - break; - case 3: - $syncRecurrence->type = 6; - } - } - // Termination - switch($recurrence->recur["term"]) { - case 0x21: - $syncRecurrence->until = $recurrence->recur["end"]; - // fixes Mantis #350 : recur-end does not consider timezones - use ClipEnd if available - if (isset($recurprops[$recurrence->proptags["enddate_recurring"]])) - $syncRecurrence->until = $recurprops[$recurrence->proptags["enddate_recurring"]]; - // add one day (minus 1 sec) to the end time to make sure the last occurrence is covered - $syncRecurrence->until += 86399; - break; - case 0x22: - $syncRecurrence->occurrences = $recurrence->recur["numoccur"]; break; - case 0x23: - // never ends - break; - } - - // Correct 'alldayevent' because outlook fails to set it on recurring items of 24 hours or longer - if(isset($recurrence->recur["endocc"], $recurrence->recur["startocc"]) && ($recurrence->recur["endocc"] - $recurrence->recur["startocc"] >= 1440)) - $syncMessage->alldayevent = true; - - // Interval is different according to the type/subtype - switch($recurrence->recur["type"]) { - case 10: - if($recurrence->recur["subtype"] == 0) - $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 1440); // minutes - break; - case 11: - case 12: - $syncRecurrence->interval = $recurrence->recur["everyn"]; - break; // months / weeks - case 13: - $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 12); - break; // months - } - - if(isset($recurrence->recur["weekdays"])) - $syncRecurrence->dayofweek = $recurrence->recur["weekdays"]; // bitmask of days (1 == sunday, 128 == saturday - if(isset($recurrence->recur["nday"])) - $syncRecurrence->weekofmonth = $recurrence->recur["nday"]; // N'th {DAY} of {X} (0-5) - if(isset($recurrence->recur["month"])) - $syncRecurrence->monthofyear = (int)($recurrence->recur["month"] / (60 * 24 * 29)) + 1; // works ok due to rounding. see also $monthminutes below (1-12) - if(isset($recurrence->recur["monthday"])) - $syncRecurrence->dayofmonth = $recurrence->recur["monthday"]; // day of month (1-31) - - // All changed exceptions are appointments within the 'exceptions' array. They contain the same items as a normal appointment - foreach($recurrence->recur["changed_occurences"] as $change) { - $exception = new SyncAppointmentException(); - - // start, end, basedate, subject, remind_before, reminderset, location, busystatus, alldayevent, label - if(isset($change["start"])) - $exception->starttime = $this->getGMTTimeByTZ($change["start"], $tz); - if(isset($change["end"])) - $exception->endtime = $this->getGMTTimeByTZ($change["end"], $tz); - if(isset($change["basedate"])) { - $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($change["basedate"]) + $recurrence->recur["startocc"] * 60, $tz); - - //open body because getting only property might not work because of memory limit - $exceptionatt = $recurrence->getExceptionAttachment($change["basedate"]); - if($exceptionatt) { - $exceptionobj = mapi_attach_openobj($exceptionatt, 0); - $this->setMessageBodyForType($exceptionobj, SYNC_BODYPREFERENCE_PLAIN, $exception); - } - } - if(isset($change["subject"])) - $exception->subject = w2u($change["subject"]); - if(isset($change["reminder_before"]) && $change["reminder_before"]) - $exception->reminder = $change["remind_before"]; - if(isset($change["location"])) - $exception->location = w2u($change["location"]); - if(isset($change["busystatus"])) - $exception->busystatus = $change["busystatus"]; - if(isset($change["alldayevent"])) - $exception->alldayevent = $change["alldayevent"]; - - // set some data from the original appointment - if (isset($syncMessage->uid)) - $exception->uid = $syncMessage->uid; - if (isset($syncMessage->organizername)) - $exception->organizername = $syncMessage->organizername; - if (isset($syncMessage->organizeremail)) - $exception->organizeremail = $syncMessage->organizeremail; - - if(!isset($syncMessage->exceptions)) - $syncMessage->exceptions = array(); - - // If the user is working from a location other than the office the busystatus should be interpreted as free. - if (isset($exception->busystatus) && $exception->busystatus == fbWorkingElsewhere) { - $exception->busystatus = fbFree; - } - - // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581 - if (isset($exception->busystatus) && $exception->busystatus == -1) { - $exception->busystatus = fbTentative; - } - - array_push($syncMessage->exceptions, $exception); - } - - // Deleted appointments contain only the original date (basedate) and a 'deleted' tag - foreach($recurrence->recur["deleted_occurences"] as $deleted) { - $exception = new SyncAppointmentException(); - - $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($deleted) + $recurrence->recur["startocc"] * 60, $tz); - $exception->deleted = "1"; - - if(!isset($syncMessage->exceptions)) - $syncMessage->exceptions = array(); - - array_push($syncMessage->exceptions, $exception); - } - - if (isset($syncMessage->complete) && $syncMessage->complete) { - $syncRecurrence->complete = $syncMessage->complete; - } - } - - /** - * Reads an email object from MAPI - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access private - * @return SyncEmail - */ - private function getEmail($mapimessage, $contentparameters) { - $message = new SyncMail(); - - $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetEmailMapping()); - - $emailproperties = MAPIMapping::GetEmailProperties(); - $messageprops = $this->getProps($mapimessage, $emailproperties); - - if(isset($messageprops[PR_SOURCE_KEY])) - $sourcekey = $messageprops[PR_SOURCE_KEY]; - else - return false; - - //set the body according to contentparameters and supported AS version - $this->setMessageBody($mapimessage, $contentparameters, $message); - - $fromname = $fromaddr = ""; - - if(isset($messageprops[$emailproperties["representingname"]])) { - // remove encapsulating double quotes from the representingname - $fromname = preg_replace('/^\"(.*)\"$/',"\${1}", $messageprops[$emailproperties["representingname"]]); - } - if(isset($messageprops[$emailproperties["representingentryid"]])) - $fromaddr = $this->getSMTPAddressFromEntryID($messageprops[$emailproperties["representingentryid"]]); - - if($fromname == $fromaddr) - $fromname = ""; - - if($fromname) - $from = "\"" . w2u($fromname) . "\" <" . w2u($fromaddr) . ">"; - else - //START CHANGED dw2412 HTC shows "error" if sender name is unknown - $from = "\"" . w2u($fromaddr) . "\" <" . w2u($fromaddr) . ">"; - //END CHANGED dw2412 HTC shows "error" if sender name is unknown - - $message->from = $from; - - // process Meeting Requests - if(isset($message->messageclass) && strpos($message->messageclass, "IPM.Schedule.Meeting") === 0) { - $message->meetingrequest = new SyncMeetingRequest(); - $this->getPropsFromMAPI($message->meetingrequest, $mapimessage, MAPIMapping::GetMeetingRequestMapping()); - - $meetingrequestproperties = MAPIMapping::GetMeetingRequestProperties(); - $props = $this->getProps($mapimessage, $meetingrequestproperties); - - // Get the GOID - if(isset($props[$meetingrequestproperties["goidtag"]])) - $message->meetingrequest->globalobjid = base64_encode($props[$meetingrequestproperties["goidtag"]]); - - // Set Timezone - if(isset($props[$meetingrequestproperties["timezonetag"]])) - $tz = $this->getTZFromMAPIBlob($props[$meetingrequestproperties["timezonetag"]]); - else - $tz = $this->getGMTTZ(); - - $message->meetingrequest->timezone = base64_encode(TimezoneUtil::GetSyncBlobFromTZ($tz)); - - // send basedate if exception - if(isset($props[$meetingrequestproperties["recReplTime"]]) || - (isset($props[$meetingrequestproperties["lidIsException"]]) && $props[$meetingrequestproperties["lidIsException"]] == true)) { - if (isset($props[$meetingrequestproperties["recReplTime"]])){ - $basedate = $props[$meetingrequestproperties["recReplTime"]]; - $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $this->getGMTTZ()); - } - else { - if (!isset($props[$meetingrequestproperties["goidtag"]]) || !isset($props[$meetingrequestproperties["recurStartTime"]]) || !isset($props[$meetingrequestproperties["timezonetag"]])) - ZLog::Write(LOGLEVEL_WARN, "Missing property to set correct basedate for exception"); - else { - $basedate = Utils::ExtractBaseDate($props[$meetingrequestproperties["goidtag"]], $props[$meetingrequestproperties["recurStartTime"]]); - $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $tz); - } - } - } - - // Organizer is the sender - if (strpos($message->messageclass, "IPM.Schedule.Meeting.Resp") === 0) { - $message->meetingrequest->organizer = $message->to; - } - else { - $message->meetingrequest->organizer = $message->from; - } - - // Process recurrence - if(isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]]) { - $myrec = new SyncMeetingRequestRecurrence(); - // get recurrence -> put $message->meetingrequest as message so the 'alldayevent' is set correctly - $this->getRecurrence($mapimessage, $props, $message->meetingrequest, $myrec, $tz); - $message->meetingrequest->recurrences = array($myrec); - } - - // Force the 'alldayevent' in the object at all times. (non-existent == 0) - if(!isset($message->meetingrequest->alldayevent) || $message->meetingrequest->alldayevent == "") - $message->meetingrequest->alldayevent = 0; - - // Instancetype - // 0 = single appointment - // 1 = master recurring appointment - // 2 = single instance of recurring appointment - // 3 = exception of recurring appointment - $message->meetingrequest->instancetype = 0; - if (isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]] == 1) - $message->meetingrequest->instancetype = 1; - else if ((!isset($props[$meetingrequestproperties["isrecurringtag"]]) || $props[$meetingrequestproperties["isrecurringtag"]] == 0 )&& isset($message->meetingrequest->recurrenceid)) - if (isset($props[$meetingrequestproperties["appSeqNr"]]) && $props[$meetingrequestproperties["appSeqNr"]] == 0 ) - $message->meetingrequest->instancetype = 2; - else - $message->meetingrequest->instancetype = 3; - - // Disable reminder if it is off - if(!isset($props[$meetingrequestproperties["reminderset"]]) || $props[$meetingrequestproperties["reminderset"]] == false) - $message->meetingrequest->reminder = ""; - //the property saves reminder in minutes, but we need it in secs - else { - ///set the default reminder time to seconds - if ($props[$meetingrequestproperties["remindertime"]] == 0x5AE980E1) - $message->meetingrequest->reminder = 900; - else - $message->meetingrequest->reminder = $props[$meetingrequestproperties["remindertime"]] * 60; - } - - // Set sensitivity to 0 if missing - if(!isset($message->meetingrequest->sensitivity)) - $message->meetingrequest->sensitivity = 0; - - // If the user is working from a location other than the office the busystatus should be interpreted as free. - if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == fbWorkingElsewhere) { - $message->meetingrequest->busystatus = fbFree; - } - - // If the busystatus has the value of -1, we should be interpreted as tentative (1) / ZP-581 - if (isset($message->meetingrequest->busystatus) && $message->meetingrequest->busystatus == -1) { - $message->meetingrequest->busystatus = fbTentative; - } - - // if a meeting request response hasn't been processed yet, - // do it so that the attendee status is updated on the mobile - if(!isset($messageprops[$emailproperties["processed"]])) { - // check if we are not sending the MR so we can process it - ZP-581 - $cuser = ZPush::GetBackend()->GetUserDetails(ZPush::GetBackend()->GetCurrentUsername()); - if(isset($cuser["emailaddress"]) && $cuser["emailaddress"] != $fromaddr) { - $req = new Meetingrequest($this->store, $mapimessage, $this->session); - if ($req->isMeetingRequestResponse()) { - $req->processMeetingRequestResponse(); - } - if ($req->isMeetingCancellation()) { - $req->processMeetingCancellation(); - } - } - } - $message->contentclass = DEFAULT_CALENDAR_CONTENTCLASS; - } - - // Add attachments - $attachtable = mapi_message_getattachmenttable($mapimessage); - $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); - $entryid = bin2hex($messageprops[$emailproperties["entryid"]]); - - foreach($rows as $row) { - if(isset($row[PR_ATTACH_NUM])) { - if (Request::GetProtocolVersion() >= 12.0) { - $attach = new SyncBaseAttachment(); - } - else { - $attach = new SyncAttachment(); - } - - $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]); - $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)); - if ((isset($attachprops[PR_ATTACH_MIME_TAG]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG]), 'signed') !== false) || - (isset($attachprops[PR_ATTACH_MIME_TAG_W]) && strpos(strtolower($attachprops[PR_ATTACH_MIME_TAG_W]), 'signed') !== false)) { - continue; - } - - // the displayname is handled equaly for all AS versions - $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"))); - // fix attachment name in case of inline images - if ($attach->displayname == "inline.txt" && (isset($attachprops[PR_ATTACH_MIME_TAG]) || $attachprops[PR_ATTACH_MIME_TAG_W])) { - $mimetype = (isset($attachprops[PR_ATTACH_MIME_TAG])) ? $attachprops[PR_ATTACH_MIME_TAG]:$attachprops[PR_ATTACH_MIME_TAG_W]; - $mime = explode("/", $mimetype); - - if (count($mime) == 2 && $mime[0] == "image") { - $attach->displayname = "inline." . $mime[1]; - } - } - - // set AS version specific parameters - if (Request::GetProtocolVersion() >= 12.0) { - $attach->filereference = $entryid.":".$row[PR_ATTACH_NUM]; - $attach->method = (isset($attachprops[PR_ATTACH_METHOD])) ? $attachprops[PR_ATTACH_METHOD] : ATTACH_BY_VALUE; - - // if displayname does not have the eml extension for embedde messages, android and WP devices won't open it - if ($attach->method == ATTACH_EMBEDDED_MSG) { - if (strtolower(substr($attach->displayname, -4)) != '.eml') - $attach->displayname .= '.eml'; - } - $attach->estimatedDataSize = $attachprops[PR_ATTACH_SIZE]; - - if (isset($attachprops[PR_ATTACH_CONTENT_ID]) && $attachprops[PR_ATTACH_CONTENT_ID]) - $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID]; - - if (!isset($attach->contentid) && isset($attachprops[PR_ATTACH_CONTENT_ID_W]) && $attachprops[PR_ATTACH_CONTENT_ID_W]) - $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID_W]; - - if (isset($attachprops[PR_ATTACHMENT_HIDDEN]) && $attachprops[PR_ATTACHMENT_HIDDEN]) $attach->isinline = 1; - - if(!isset($message->asattachments)) - $message->asattachments = array(); - - array_push($message->asattachments, $attach); - } - else { - $attach->attsize = $attachprops[PR_ATTACH_SIZE]; - $attach->attname = $entryid.":".$row[PR_ATTACH_NUM]; - if(!isset($message->attachments)) - $message->attachments = array(); - - array_push($message->attachments, $attach); - } - } - } - - // Get To/Cc as SMTP addresses (this is different from displayto and displaycc because we are putting - // in the SMTP addresses as well, while displayto and displaycc could just contain the display names - $message->to = array(); - $message->cc = array(); - - $reciptable = mapi_message_getrecipienttable($mapimessage); - $rows = mapi_table_queryallrows($reciptable, array(PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ENTRYID)); - - foreach ($rows as $row) { - $address = ""; - $fulladdr = ""; - - $addrtype = isset($row[PR_ADDRTYPE]) ? $row[PR_ADDRTYPE] : ""; - - if (isset($row[PR_SMTP_ADDRESS])) - $address = $row[PR_SMTP_ADDRESS]; - elseif ($addrtype == "SMTP" && isset($row[PR_EMAIL_ADDRESS])) - $address = $row[PR_EMAIL_ADDRESS]; - elseif ($addrtype == "ZARAFA" && isset($row[PR_ENTRYID])) - $address = $this->getSMTPAddressFromEntryID($row[PR_ENTRYID]); - - $name = isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : ""; - - if($name == "" || $name == $address) - $fulladdr = w2u($address); - else { - if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') { - $fulladdr = "\"" . w2u($name) ."\" <" . w2u($address) . ">"; - } - else { - $fulladdr = w2u($name) ."<" . w2u($address) . ">"; - } - } - - if($row[PR_RECIPIENT_TYPE] == MAPI_TO) { - array_push($message->to, $fulladdr); - } else if($row[PR_RECIPIENT_TYPE] == MAPI_CC) { - array_push($message->cc, $fulladdr); - } - } - - if (is_array($message->to) && !empty($message->to)) $message->to = implode(", ", $message->to); - if (is_array($message->cc) && !empty($message->cc)) $message->cc = implode(", ", $message->cc); - - // without importance some mobiles assume "0" (low) - Mantis #439 - if (!isset($message->importance)) - $message->importance = IMPORTANCE_NORMAL; - - //TODO contentclass and nativebodytype and internetcpid - if (!isset($message->internetcpid)) $message->internetcpid = (defined('STORE_INTERNET_CPID')) ? constant('STORE_INTERNET_CPID') : INTERNET_CPID_WINDOWS1252; - $this->setFlag($mapimessage, $message); - if (!isset($message->contentclass)) $message->contentclass = DEFAULT_EMAIL_CONTENTCLASS; - if (!isset($message->nativebodytype)) $message->nativebodytype = $this->getNativeBodyType($messageprops); - - // reply, reply to all, forward flags - if (isset($message->lastverbexecuted) && $message->lastverbexecuted) { - $message->lastverbexecuted = Utils::GetLastVerbExecuted($message->lastverbexecuted); - } - - // OL 2013 doesn't show sender and subject for signed emails because the headers are missing - if(isset($message->messageclass) && strpos($message->messageclass, "IPM.Note.SMIME.MultipartSigned") === 0 && - isset($message->asbody->type) && $message->asbody->type == SYNC_BODYPREFERENCE_MIME) { - ZLog::Write(LOGLEVEL_DEBUG, "Attach the transport message headers to a signed message"); - $transportHeaders = array(PR_TRANSPORT_MESSAGE_HEADERS_W); - $messageHeaders = $this->getProps($mapimessage, $transportHeaders); - $message->asbody->data = $messageHeaders[PR_TRANSPORT_MESSAGE_HEADERS] ."\r\n\r\n" . $message->asbody->data; - } - - return $message; - } - - /** - * Reads a note object from MAPI - * - * @param mixed $mapimessage - * @param ContentParameters $contentparameters - * - * @access private - * @return SyncNote - */ - private function getNote($mapimessage, $contentparameters) { - $message = new SyncNote(); - - // Standard one-to-one mappings first - $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetNoteMapping()); - - //set the body according to contentparameters and supported AS version - $this->setMessageBody($mapimessage, $contentparameters, $message); - - return $message; - } - - /** - * Creates a SyncFolder from MAPI properties. - * - * @param mixed $folderprops - * - * @access public - * @return SyncFolder - */ - public function GetFolder($folderprops) { - $folder = new SyncFolder(); - - $storeprops = $this->getStoreProps(); - - if(!isset($folderprops[PR_DISPLAY_NAME]) || - !isset($folderprops[PR_PARENT_ENTRYID]) || - !isset($folderprops[PR_SOURCE_KEY]) || - !isset($folderprops[PR_ENTRYID]) || - !isset($folderprops[PR_PARENT_SOURCE_KEY]) || - !isset($storeprops[PR_IPM_SUBTREE_ENTRYID])) { - ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->GetFolder(): invalid folder. Missing properties"); - return false; - } - - // ignore hidden folders - if (isset($folderprops[PR_ATTR_HIDDEN]) && $folderprops[PR_ATTR_HIDDEN] != false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): invalid folder '%s' as it is a hidden folder (PR_ATTR_HIDDEN)", $folderprops[PR_DISPLAY_NAME])); - return false; - } - - // ignore certain undesired folders, like "RSS Feeds" - if (isset($folderprops[PR_CONTAINER_CLASS]) && $folderprops[PR_CONTAINER_CLASS] == "IPF.Note.OutlookHomepage") { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): folder '%s' should not be synchronized", $folderprops[PR_DISPLAY_NAME])); - return false; - } - - $folder->serverid = bin2hex($folderprops[PR_SOURCE_KEY]); - if($folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_SUBTREE_ENTRYID]) - $folder->parentid = "0"; - else - $folder->parentid = bin2hex($folderprops[PR_PARENT_SOURCE_KEY]); - $folder->displayname = w2u($folderprops[PR_DISPLAY_NAME]); - $folder->type = $this->GetFolderType($folderprops[PR_ENTRYID], isset($folderprops[PR_CONTAINER_CLASS])?$folderprops[PR_CONTAINER_CLASS]:false); - - return $folder; - } - - /** - * Returns the foldertype for an entryid - * Gets the folder type by checking the default folders in MAPI - * - * @param string $entryid - * @param string $class (opt) - * - * @access public - * @return long - */ - public function GetFolderType($entryid, $class = false) { - $storeprops = $this->getStoreProps(); - $inboxprops = $this->getInboxProps(); - - if($entryid == $inboxprops[PR_ENTRYID]) - return SYNC_FOLDER_TYPE_INBOX; - if($entryid == $inboxprops[PR_IPM_DRAFTS_ENTRYID]) - return SYNC_FOLDER_TYPE_DRAFTS; - if($entryid == $storeprops[PR_IPM_WASTEBASKET_ENTRYID]) - return SYNC_FOLDER_TYPE_WASTEBASKET; - if($entryid == $storeprops[PR_IPM_SENTMAIL_ENTRYID]) - return SYNC_FOLDER_TYPE_SENTMAIL; - if($entryid == $storeprops[PR_IPM_OUTBOX_ENTRYID]) - return SYNC_FOLDER_TYPE_OUTBOX; - if($entryid == $inboxprops[PR_IPM_TASK_ENTRYID]) - return SYNC_FOLDER_TYPE_TASK; - if($entryid == $inboxprops[PR_IPM_APPOINTMENT_ENTRYID]) - return SYNC_FOLDER_TYPE_APPOINTMENT; - if($entryid == $inboxprops[PR_IPM_CONTACT_ENTRYID]) - return SYNC_FOLDER_TYPE_CONTACT; - if($entryid == $inboxprops[PR_IPM_NOTE_ENTRYID]) - return SYNC_FOLDER_TYPE_NOTE; - if($entryid == $inboxprops[PR_IPM_JOURNAL_ENTRYID]) - return SYNC_FOLDER_TYPE_JOURNAL; - - // user created folders - if ($class == "IPF.Note") - return SYNC_FOLDER_TYPE_USER_MAIL; - if ($class == "IPF.Task") - return SYNC_FOLDER_TYPE_USER_TASK; - if ($class == "IPF.Appointment") - return SYNC_FOLDER_TYPE_USER_APPOINTMENT; - if ($class == "IPF.Contact") - return SYNC_FOLDER_TYPE_USER_CONTACT; - if ($class == "IPF.StickyNote") - return SYNC_FOLDER_TYPE_USER_NOTE; - if ($class == "IPF.Journal") - return SYNC_FOLDER_TYPE_USER_JOURNAL; - - return SYNC_FOLDER_TYPE_OTHER; - } - - /** - * Indicates if the entry id is a default MAPI folder - * - * @param string $entryid - * - * @access public - * @return boolean - */ - public function IsMAPIDefaultFolder($entryid) { - $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)); - - $inboxProps = array(); - $inbox = mapi_msgstore_getreceivefolder($this->store); - if(!mapi_last_hresult()) - $inboxProps = mapi_getprops($inbox, array(PR_ENTRYID)); - - $root = mapi_msgstore_openentry($this->store, null); - $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)); - - $additional_ren_entryids = array(); - if(isset($rootProps[PR_ADDITIONAL_REN_ENTRYIDS])) - $additional_ren_entryids = $rootProps[PR_ADDITIONAL_REN_ENTRYIDS]; - - $defaultfolders = array( - "inbox" => array("inbox"=>PR_ENTRYID), - "outbox" => array("store"=>PR_IPM_OUTBOX_ENTRYID), - "sent" => array("store"=>PR_IPM_SENTMAIL_ENTRYID), - "wastebasket" => array("store"=>PR_IPM_WASTEBASKET_ENTRYID), - "favorites" => array("store"=>PR_IPM_FAVORITES_ENTRYID), - "publicfolders" => array("store"=>PR_IPM_PUBLIC_FOLDERS_ENTRYID), - "calendar" => array("root" =>PR_IPM_APPOINTMENT_ENTRYID), - "contact" => array("root" =>PR_IPM_CONTACT_ENTRYID), - "drafts" => array("root" =>PR_IPM_DRAFTS_ENTRYID), - "journal" => array("root" =>PR_IPM_JOURNAL_ENTRYID), - "note" => array("root" =>PR_IPM_NOTE_ENTRYID), - "task" => array("root" =>PR_IPM_TASK_ENTRYID), - "junk" => array("additional" =>4), - "syncissues" => array("additional" =>1), - "conflicts" => array("additional" =>0), - "localfailures" => array("additional" =>2), - "serverfailures" => array("additional" =>3), - ); - - foreach($defaultfolders as $key=>$prop){ - $tag = reset($prop); - $from = key($prop); - switch($from){ - case "inbox": - if(isset($inboxProps[$tag]) && $entryid == $inboxProps[$tag]) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Inbox found, key '%s'", $key)); - return true; - } - break; - case "store": - if(isset($msgstore_props[$tag]) && $entryid == $msgstore_props[$tag]) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Store folder found, key '%s'", $key)); - return true; - } - break; - case "root": - if(isset($rootProps[$tag]) && $entryid == $rootProps[$tag]) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Root folder found, key '%s'", $key)); - return true; - } - break; - case "additional": - if(isset($additional_ren_entryids[$tag]) && $entryid == $additional_ren_entryids[$tag]) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->IsMAPIFolder(): Additional folder found, key '%s'", $key)); - return true; - } - } - } - return false; - } - - /**---------------------------------------------------------------------------------------------------------- - * SETTER - */ - - /** - * Writes a SyncObject to MAPI - * Depending on the message class, a contact, appointment, task or email is written - * - * @param mixed $mapimessage - * @param SyncObject $message - * - * @access public - * @return boolean - */ - public function SetMessage($mapimessage, $message) { - // TODO check with instanceof - switch(strtolower(get_class($message))) { - case "synccontact": - return $this->setContact($mapimessage, $message); - case "syncappointment": - return $this->setAppointment($mapimessage, $message); - case "synctask": - return $this->setTask($mapimessage, $message); - case "syncnote": - return $this->setNote($mapimessage, $message); - default: - //for emails only flag (read and todo) changes are possible - return $this->setEmail($mapimessage, $message); - } - } - - /** - * Writes SyncMail to MAPI (actually flags only) - * - * @param mixed $mapimessage - * @param SyncMail $message - */ - private function setEmail($mapimessage, $message) { - $flagmapping = MAPIMapping::GetMailFlagsMapping(); - $flagprops = MAPIMapping::GetMailFlagsProperties(); - $flagprops = array_merge($this->getPropIdsFromStrings($flagmapping), $this->getPropIdsFromStrings($flagprops)); - // flag specific properties to be set - $props = $delprops = array(); - // unset message flags if: - // flag is not set - if (empty($message->flag) || - // flag status is not set - !isset($message->flag->flagstatus) || - // flag status is 0 or empty - (isset($message->flag->flagstatus) && ($message->flag->flagstatus == 0 || $message->flag->flagstatus == "")) ) { - // if message flag is empty, some properties need to be deleted - // and some set to 0 or false - - $props[$flagprops["todoitemsflags"]] = 0; - $props[$flagprops["status"]] = 0; - $props[$flagprops["completion"]] = 0.0; - $props[$flagprops["flagtype"]] = ""; - $props[$flagprops["ordinaldate"]] = 0x7fffffff; // ordinal date is 12am 1.1.4501, set it to max possible value - $props[$flagprops["subordinaldate"]] = ""; - $props[$flagprops["replyrequested"]] = false; - $props[$flagprops["responserequested"]] = false; - $props[$flagprops["reminderset"]] = false; - $props[$flagprops["complete"]] = false; - - $delprops[] = $flagprops["todotitle"]; - $delprops[] = $flagprops["duedate"]; - $delprops[] = $flagprops["startdate"]; - $delprops[] = $flagprops["datecompleted"]; - $delprops[] = $flagprops["utcstartdate"]; - $delprops[] = $flagprops["utcduedate"]; - $delprops[] = $flagprops["completetime"]; - $delprops[] = $flagprops["flagstatus"]; - $delprops[] = $flagprops["flagicon"]; - } - else { - $this->setPropsInMAPI($mapimessage, $message->flag, $flagmapping); - $props[$flagprops["todoitemsflags"]] = 1; - if (isset($message->subject) && str_len($message->subject) > 0) - $props[$flagprops["todotitle"]] = $message->subject; - // ordinal date is utc current time - if (!isset($message->flag->ordinaldate) || empty($message->flag->ordinaldate)) { - $props[$flagprops["ordinaldate"]] = time(); - } - // the default value - if (!isset($message->flag->subordinaldate) || empty($message->flag->subordinaldate)) { - $props[$flagprops["subordinaldate"]] = "5555555"; - } - $props[$flagprops["flagicon"]] = 6; //red flag icon - $props[$flagprops["replyrequested"]] = true; - $props[$flagprops["responserequested"]] = true; - - if ($message->flag->flagstatus == SYNC_FLAGSTATUS_COMPLETE) { - $props[$flagprops["status"]] = olTaskComplete; - $props[$flagprops["completion"]] = 1.0; - $props[$flagprops["complete"]] = true; - $props[$flagprops["replyrequested"]] = false; - $props[$flagprops["responserequested"]] = false; - unset($props[$flagprops["flagicon"]]); - $delprops[] = $flagprops["flagicon"]; - } - } - - if (!empty($props)) { - mapi_setprops($mapimessage, $props); - } - if (!empty($delprops)) { - mapi_deleteprops($mapimessage, $delprops); - } - } - - /** - * Writes a SyncAppointment to MAPI - * - * @param mixed $mapimessage - * @param SyncAppointment $message - * - * @access private - * @return boolean - */ - private function setAppointment($mapimessage, $appointment) { - // Get timezone info - if(isset($appointment->timezone)) - $tz = $this->getTZFromSyncBlob(base64_decode($appointment->timezone)); - else - $tz = false; - - //calculate duration because without it some webaccess views are broken. duration is in min - $localstart = $this->getLocaltimeByTZ($appointment->starttime, $tz); - $localend = $this->getLocaltimeByTZ($appointment->endtime, $tz); - $duration = ($localend - $localstart)/60; - - //nokia sends an yearly event with 0 mins duration but as all day event, - //so make it end next day - if ($appointment->starttime == $appointment->endtime && isset($appointment->alldayevent) && $appointment->alldayevent) { - $duration = 1440; - $appointment->endtime = $appointment->starttime + 24 * 60 * 60; - $localend = $localstart + 24 * 60 * 60; - } - - // is the transmitted UID OL compatible? - // if not, encapsulate the transmitted uid - $appointment->uid = Utils::GetOLUidFromICalUid($appointment->uid); - - mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment")); - - $appointmentmapping = MAPIMapping::GetAppointmentMapping(); - $this->setPropsInMAPI($mapimessage, $appointment, $appointmentmapping); - $appointmentprops = MAPIMapping::GetAppointmentProperties(); - $appointmentprops = array_merge($this->getPropIdsFromStrings($appointmentmapping), $this->getPropIdsFromStrings($appointmentprops)); - //appointment specific properties to be set - $props = array(); - - //we also have to set the responsestatus and not only meetingstatus, so we use another mapi tag - $props[$appointmentprops["responsestatus"]] = (isset($appointment->responsestatus)) ? $appointment->responsestatus : olResponseNone; - - //sensitivity is not enough to mark an appointment as private, so we use another mapi tag - $private = (isset($appointment->sensitivity) && $appointment->sensitivity == 0) ? false : true; - - // Set commonstart/commonend to start/end and remindertime to start, duration, private and cleanGlobalObjectId - $props[$appointmentprops["commonstart"]] = $appointment->starttime; - $props[$appointmentprops["commonend"]] = $appointment->endtime; - $props[$appointmentprops["reminderstart"]] = $appointment->starttime; - // Set reminder boolean to 'true' if reminder is set - $props[$appointmentprops["reminderset"]] = isset($appointment->reminder) ? true : false; - $props[$appointmentprops["duration"]] = $duration; - $props[$appointmentprops["private"]] = $private; - $props[$appointmentprops["uid"]] = $appointment->uid; - // Set named prop 8510, unknown property, but enables deleting a single occurrence of a recurring - // type in OLK2003. - $props[$appointmentprops["sideeffects"]] = 369; - - - if(isset($appointment->reminder) && $appointment->reminder >= 0) { - // Set 'flagdueby' to correct value (start - reminderminutes) - $props[$appointmentprops["flagdueby"]] = $appointment->starttime - $appointment->reminder * 60; - $props[$appointmentprops["remindertime"]] = $appointment->reminder; - } - // unset the reminder - else { - $props[$appointmentprops["reminderset"]] = false; - } - - if (isset($appointment->asbody)) { - $this->setASbody($appointment->asbody, $props, $appointmentprops); - } - - if(isset($appointment->recurrence)) { - // Set PR_ICON_INDEX to 1025 to show correct icon in category view - $props[$appointmentprops["icon"]] = 1025; - - //if there aren't any exceptions, use the 'old style' set recurrence - $noexceptions = true; - - $recurrence = new Recurrence($this->store, $mapimessage); - $recur = array(); - $this->setRecurrence($appointment, $recur); - - // set the recurrence type to that of the MAPI - $props[$appointmentprops["recurrencetype"]] = $recur["recurrencetype"]; - - $starttime = $this->gmtime($localstart); - $endtime = $this->gmtime($localend); - - //set recurrence start here because it's calculated differently for tasks and appointments - $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, $tz)); - - $recur["startocc"] = $starttime["tm_hour"] * 60 + $starttime["tm_min"]; - $recur["endocc"] = $recur["startocc"] + $duration; // Note that this may be > 24*60 if multi-day - - //only tasks can regenerate - $recur["regen"] = false; - - // Process exceptions. The PDA will send all exceptions for this recurring item. - if(isset($appointment->exceptions)) { - foreach($appointment->exceptions as $exception) { - // we always need the base date - if(!isset($exception->exceptionstarttime)) - continue; - - if(isset($exception->deleted) && $exception->deleted) { - // Delete exception - if(!isset($recur["deleted_occurences"])) - $recur["deleted_occurences"] = array(); - - array_push($recur["deleted_occurences"], $this->getDayStartOfTimestamp($exception->exceptionstarttime)); - } else { - // Change exception - $basedate = $this->getDayStartOfTimestamp($exception->exceptionstarttime); - $mapiexception = array("basedate" => $basedate); - //other exception properties which are not handled in recurrence - $exceptionprops = array(); - - if(isset($exception->starttime)) { - $mapiexception["start"] = $this->getLocaltimeByTZ($exception->starttime, $tz); - $exceptionprops[$appointmentprops["starttime"]] = $exception->starttime; - } - if(isset($exception->endtime)) { - $mapiexception["end"] = $this->getLocaltimeByTZ($exception->endtime, $tz); - $exceptionprops[$appointmentprops["endtime"]] = $exception->endtime; - } - if(isset($exception->subject)) - $exceptionprops[$appointmentprops["subject"]] = $mapiexception["subject"] = u2w($exception->subject); - if(isset($exception->location)) - $exceptionprops[$appointmentprops["location"]] = $mapiexception["location"] = u2w($exception->location); - if(isset($exception->busystatus)) - $exceptionprops[$appointmentprops["busystatus"]] = $mapiexception["busystatus"] = $exception->busystatus; - if(isset($exception->reminder)) { - $exceptionprops[$appointmentprops["reminderset"]] = $mapiexception["reminder_set"] = 1; - $exceptionprops[$appointmentprops["remindertime"]] = $mapiexception["remind_before"] = $exception->reminder; - } - if(isset($exception->alldayevent)) - $exceptionprops[$appointmentprops["alldayevent"]] = $mapiexception["alldayevent"] = $exception->alldayevent; - - - if(!isset($recur["changed_occurences"])) - $recur["changed_occurences"] = array(); - - if (isset($exception->body)) - $exceptionprops[$appointmentprops["body"]] = u2w($exception->body); - - if (isset($exception->asbody)) { - $this->setASbody($exception->asbody, $exceptionprops, $appointmentprops); - $mapiexception["body"] = $exceptionprops[$appointmentprops["body"]] = - (isset($exceptionprops[$appointmentprops["body"]])) ? $exceptionprops[$appointmentprops["body"]] : - ((isset($exceptionprops[$appointmentprops["html"]])) ? $exceptionprops[$appointmentprops["html"]] : ""); - } - - array_push($recur["changed_occurences"], $mapiexception); - - if (!empty($exceptionprops)) { - $noexceptions = false; - if($recurrence->isException($basedate)){ - $recurrence->modifyException($exceptionprops, $basedate); - } - else { - $recurrence->createException($exceptionprops, $basedate); - } - } - - } - } - } - - //setRecurrence deletes the attachments from an appointment - if ($noexceptions) { - $recurrence->setRecurrence($tz, $recur); - } - } - else { - $props[$appointmentprops["isrecurring"]] = false; - } - - //always set the PR_SENT_REPRESENTING_* props so that the attendee status update also works with the webaccess - $p = array( $appointmentprops["representingentryid"], $appointmentprops["representingname"], $appointmentprops["sentrepresentingaddt"], - $appointmentprops["sentrepresentingemail"], $appointmentprops["sentrepresentinsrchk"]); - $representingprops = $this->getProps($mapimessage, $p); - - if (!isset($representingprops[$appointmentprops["representingentryid"]])) { - // TODO use getStoreProps - $storeProps = mapi_getprops($this->store, array(PR_MAILBOX_OWNER_ENTRYID)); - $props[$appointmentprops["representingentryid"]] = $storeProps[PR_MAILBOX_OWNER_ENTRYID]; - $displayname = $this->getFullnameFromEntryID($storeProps[PR_MAILBOX_OWNER_ENTRYID]); - - $props[$appointmentprops["representingname"]] = ($displayname !== false) ? $displayname : Request::GetAuthUser(); - $props[$appointmentprops["sentrepresentingemail"]] = Request::GetAuthUser(); - $props[$appointmentprops["sentrepresentingaddt"]] = "ZARAFA"; - $props[$appointmentprops["sentrepresentinsrchk"]] = $props[$appointmentprops["sentrepresentingaddt"]].":".$props[$appointmentprops["sentrepresentingemail"]]; - - if(isset($appointment->attendees) && is_array($appointment->attendees) && !empty($appointment->attendees)) { - $props[$appointmentprops["icon"]] = 1026; - // the user is the organizer - // set these properties to show tracking tab in webapp - - $props[$appointmentprops["mrwassent"]] = true; - $props[$appointmentprops["responsestatus"]] = olResponseOrganized; - $props[$appointmentprops["meetingstatus"]] = olMeeting; - } - } - - // Do attendees - if(isset($appointment->attendees) && is_array($appointment->attendees)) { - $recips = array(); - - // Outlook XP requires organizer in the attendee list as well - $org = array(); - $org[PR_ENTRYID] = isset($representingprops[$appointmentprops["representingentryid"]]) ? $representingprops[$appointmentprops["representingentryid"]] : $props[$appointmentprops["representingentryid"]]; - $org[PR_DISPLAY_NAME] = isset($representingprops[$appointmentprops["representingname"]]) ? $representingprops[$appointmentprops["representingname"]] : $props[$appointmentprops["representingname"]]; - $org[PR_ADDRTYPE] = isset($representingprops[$appointmentprops["sentrepresentingaddt"]]) ? $representingprops[$appointmentprops["sentrepresentingaddt"]] : $props[$appointmentprops["sentrepresentingaddt"]]; - $org[PR_EMAIL_ADDRESS] = isset($representingprops[$appointmentprops["sentrepresentingemail"]]) ? $representingprops[$appointmentprops["sentrepresentingemail"]] : $props[$appointmentprops["sentrepresentingemail"]]; - $org[PR_SEARCH_KEY] = isset($representingprops[$appointmentprops["sentrepresentinsrchk"]]) ? $representingprops[$appointmentprops["sentrepresentinsrchk"]] : $props[$appointmentprops["sentrepresentinsrchk"]]; - $org[PR_RECIPIENT_FLAGS] = recipOrganizer | recipSendable; - $org[PR_RECIPIENT_TYPE] = MAPI_TO; - - array_push($recips, $org); - - //open addresss book for user resolve - $addrbook = $this->getAddressbook(); - foreach($appointment->attendees as $attendee) { - $recip = array(); - $recip[PR_EMAIL_ADDRESS] = u2w($attendee->email); - - // lookup information in GAB if possible so we have up-to-date name for given address - $userinfo = array( array( PR_DISPLAY_NAME => $recip[PR_EMAIL_ADDRESS] ) ); - $userinfo = mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP); - if(mapi_last_hresult() == NOERROR) { - $recip[PR_DISPLAY_NAME] = $userinfo[0][PR_DISPLAY_NAME]; - $recip[PR_EMAIL_ADDRESS] = $userinfo[0][PR_EMAIL_ADDRESS]; - $recip[PR_SEARCH_KEY] = $userinfo[0][PR_SEARCH_KEY]; - $recip[PR_ADDRTYPE] = $userinfo[0][PR_ADDRTYPE]; - $recip[PR_ENTRYID] = $userinfo[0][PR_ENTRYID]; - $recip[PR_RECIPIENT_TYPE] = MAPI_TO; - $recip[PR_RECIPIENT_FLAGS] = recipSendable; - } - else { - $recip[PR_DISPLAY_NAME] = u2w($attendee->name); - $recip[PR_SEARCH_KEY] = "SMTP:".$recip[PR_EMAIL_ADDRESS]."\0"; - $recip[PR_ADDRTYPE] = "SMTP"; - $recip[PR_RECIPIENT_TYPE] = MAPI_TO; - $recip[PR_ENTRYID] = mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]); - } - - array_push($recips, $recip); - } - - mapi_message_modifyrecipients($mapimessage, 0, $recips); - } - mapi_setprops($mapimessage, $props); - } - - /** - * Writes a SyncContact to MAPI - * - * @param mixed $mapimessage - * @param SyncContact $contact - * - * @access private - * @return boolean - */ - private function setContact($mapimessage, $contact) { - mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Contact")); - - // normalize email addresses - if (isset($contact->email1address) && (($contact->email1address = $this->extractEmailAddress($contact->email1address)) === false)) - unset($contact->email1address); - - if (isset($contact->email2address) && (($contact->email2address = $this->extractEmailAddress($contact->email2address)) === false)) - unset($contact->email2address); - - if (isset($contact->email3address) && (($contact->email3address = $this->extractEmailAddress($contact->email3address)) === false)) - unset($contact->email3address); - - $contactmapping = MAPIMapping::GetContactMapping(); - $contactprops = MAPIMapping::GetContactProperties(); - $this->setPropsInMAPI($mapimessage, $contact, $contactmapping); - - ///set display name from contact's properties - $cname = $this->composeDisplayName($contact); - - //get contact specific mapi properties and merge them with the AS properties - $contactprops = array_merge($this->getPropIdsFromStrings($contactmapping), $this->getPropIdsFromStrings($contactprops)); - - //contact specific properties to be set - $props = array(); - - //need to be set in order to show contacts properly in outlook and wa - $nremails = array(); - $abprovidertype = 0; - - if (isset($contact->email1address)) - $this->setEmailAddress($contact->email1address, $cname, 1, $props, $contactprops, $nremails, $abprovidertype); - if (isset($contact->email2address)) - $this->setEmailAddress($contact->email2address, $cname, 2, $props, $contactprops, $nremails, $abprovidertype); - if (isset($contact->email3address)) - $this->setEmailAddress($contact->email3address, $cname, 3, $props, $contactprops, $nremails, $abprovidertype); - - $props[$contactprops["addressbooklong"]] = $abprovidertype; - $props[$contactprops["displayname"]] = $props[$contactprops["subject"]] = $cname; - - //pda multiple e-mail addresses bug fix for the contact - if (!empty($nremails)) $props[$contactprops["addressbookmv"]] = $nremails; - - - //set addresses - $this->setAddress("home", $contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props, $contactprops); - $this->setAddress("business", $contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props, $contactprops); - $this->setAddress("other", $contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props, $contactprops); - - //set the mailing address and its type - if (isset($props[$contactprops["businessaddress"]])) { - $props[$contactprops["mailingaddress"]] = 2; - $this->setMailingAddress($contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props[$contactprops["businessaddress"]], $props, $contactprops); - } - elseif (isset($props[$contactprops["homeaddress"]])) { - $props[$contactprops["mailingaddress"]] = 1; - $this->setMailingAddress($contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props[$contactprops["homeaddress"]], $props, $contactprops); - } - elseif (isset($props[$contactprops["otheraddress"]])) { - $props[$contactprops["mailingaddress"]] = 3; - $this->setMailingAddress($contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props[$contactprops["otheraddress"]], $props, $contactprops); - } - - if (isset($contact->picture)) { - $picbinary = base64_decode($contact->picture); - $picsize = strlen($picbinary); - $props[$contactprops["haspic"]] = false; - - // TODO contact picture handling - // check if contact has already got a picture. delete it first in that case - // delete it also if it was removed on a mobile - $picprops = mapi_getprops($mapimessage, array($contactprops["haspic"])); - if (isset($picprops[$contactprops["haspic"]]) && $picprops[$contactprops["haspic"]]) { - ZLog::Write(LOGLEVEL_DEBUG, "Contact already has a picture. Delete it"); - - $attachtable = mapi_message_getattachmenttable($mapimessage); - mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction()); - $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); - if (isset($rows) && is_array($rows)) { - foreach ($rows as $row) { - mapi_message_deleteattach($mapimessage, $row[PR_ATTACH_NUM]); - } - } - } - - // only set picture if there's data in the request - if ($picbinary !== false && $picsize > 0) { - $props[$contactprops["haspic"]] = true; - $pic = mapi_message_createattach($mapimessage); - // Set properties of the attachment - $picprops = array( - PR_ATTACH_LONG_FILENAME_A => "ContactPicture.jpg", - PR_DISPLAY_NAME => "ContactPicture.jpg", - 0x7FFF000B => true, - PR_ATTACHMENT_HIDDEN => false, - PR_ATTACHMENT_FLAGS => 1, - PR_ATTACH_METHOD => ATTACH_BY_VALUE, - PR_ATTACH_EXTENSION_A => ".jpg", - PR_ATTACH_NUM => 1, - PR_ATTACH_SIZE => $picsize, - PR_ATTACH_DATA_BIN => $picbinary, - ); - - mapi_setprops($pic, $picprops); - mapi_savechanges($pic); - } - } - - if (isset($contact->asbody)) { - $this->setASbody($contact->asbody, $props, $contactprops); - } - - //set fileas - if (defined('FILEAS_ORDER')) { - $lastname = (isset($contact->lastname)) ? $contact->lastname : ""; - $firstname = (isset($contact->firstname)) ? $contact->firstname : ""; - $middlename = (isset($contact->middlename)) ? $contact->middlename : ""; - $company = (isset($contact->companyname)) ? $contact->companyname : ""; - $props[$contactprops["fileas"]] = Utils::BuildFileAs($lastname, $firstname, $middlename, $company); - } - else ZLog::Write(LOGLEVEL_DEBUG, "FILEAS_ORDER not defined"); - - mapi_setprops($mapimessage, $props); - } - - /** - * Writes a SyncTask to MAPI - * - * @param mixed $mapimessage - * @param SyncTask $task - * - * @access private - * @return boolean - */ - private function setTask($mapimessage, $task) { - mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Task")); - - $taskmapping = MAPIMapping::GetTaskMapping(); - $taskprops = MAPIMapping::GetTaskProperties(); - $this->setPropsInMAPI($mapimessage, $task, $taskmapping); - $taskprops = array_merge($this->getPropIdsFromStrings($taskmapping), $this->getPropIdsFromStrings($taskprops)); - - // task specific properties to be set - $props = array(); - - if (isset($task->asbody)) { - $this->setASbody($task->asbody, $props, $taskprops); - } - - if(isset($task->complete)) { - if($task->complete) { - // Set completion to 100% - // Set status to 'complete' - $props[$taskprops["completion"]] = 1.0; - $props[$taskprops["status"]] = 2; - $props[$taskprops["reminderset"]] = false; - } else { - // Set completion to 0% - // Set status to 'not started' - $props[$taskprops["completion"]] = 0.0; - $props[$taskprops["status"]] = 0; - } - } - if (isset($task->recurrence) && class_exists('TaskRecurrence')) { - $deadoccur = false; - if ((isset($task->recurrence->occurrences) && $task->recurrence->occurrences == 1) || - (isset($task->recurrence->deadoccur) && $task->recurrence->deadoccur == 1)) //ios5 sends deadoccur inside the recurrence - $deadoccur = true; - - // Set PR_ICON_INDEX to 1281 to show correct icon in category view - $props[$taskprops["icon"]] = 1281; - // dead occur - false if new occurrences should be generated from the task - // true - if it is the last ocurrence of the task - $props[$taskprops["deadoccur"]] = $deadoccur; - $props[$taskprops["isrecurringtag"]] = true; - - $recurrence = new TaskRecurrence($this->store, $mapimessage); - $recur = array(); - $this->setRecurrence($task, $recur); - - // task specific recurrence properties which we need to set here - // "start" and "end" are in GMT when passing to class.recurrence - // set recurrence start here because it's calculated differently for tasks and appointments - $recur["start"] = $task->recurrence->start; - $recur["regen"] = (isset($task->recurrence->regenerate) && $task->recurrence->regenerate) ? 1 : 0; - //Also add dates to $recur - $recur["duedate"] = $task->duedate; - $recur["complete"] = (isset($task->complete) && $task->complete) ? 1 : 0; - $recurrence->setRecurrence($recur); - } - - //open addresss book for user resolve to set the owner - $addrbook = $this->getAddressbook(); - - // check if there is already an owner for the task, set current user if not - $p = array( $taskprops["owner"]); - $owner = $this->getProps($mapimessage, $p); - if (!isset($owner[$taskprops["owner"]])) { - $userinfo = mapi_zarafa_getuser($this->store, Request::GetAuthUser()); - if(mapi_last_hresult() == NOERROR && isset($userinfo["fullname"])) { - $props[$taskprops["owner"]] = $userinfo["fullname"]; - } - } - mapi_setprops($mapimessage, $props); - - - } - - /** - * Writes a SyncNote to MAPI - * - * @param mixed $mapimessage - * @param SyncNote $note - * - * @access private - * @return boolean - */ - private function setNote($mapimessage, $note) { - // Touchdown does not send categories if all are unset or there is none. - // Setting it to an empty array will unset the property in Zarafa as well - if (!isset($note->categories)) $note->categories = array(); - - $this->setPropsInMAPI($mapimessage, $note, MAPIMapping::GetNoteMapping()); - - $noteprops = MAPIMapping::GetNoteProperties(); - $noteprops = $this->getPropIdsFromStrings($noteprops); - - // note specific properties to be set - $props = array(); - $props[$noteprops["messageclass"]] = "IPM.StickyNote"; - // set body otherwise the note will be "broken" when editing it in outlook - $this->setASbody($note->asbody, $props, $noteprops); - - $props[$noteprops["internetcpid"]] = INTERNET_CPID_UTF8; - mapi_setprops($mapimessage, $props); - } - - /**---------------------------------------------------------------------------------------------------------- - * HELPER - */ - - /** - * Returns the tiemstamp offset - * - * @param string $ts - * - * @access private - * @return long - */ - private function GetTZOffset($ts) { - $Offset = date("O", $ts); - - $Parity = $Offset < 0 ? -1 : 1; - $Offset = $Parity * $Offset; - $Offset = ($Offset - ($Offset % 100)) / 100 * 60 + $Offset % 100; - - return $Parity * $Offset; - } - - /** - * Localtime of the timestamp - * - * @param long $time - * - * @access private - * @return array - */ - private function gmtime($time) { - $TZOffset = $this->GetTZOffset($time); - - $t_time = $time - $TZOffset * 60; #Counter adjust for localtime() - $t_arr = localtime($t_time, 1); - - return $t_arr; - } - - /** - * Sets the properties in a MAPI object according to an Sync object and a property mapping - * - * @param mixed $mapimessage - * @param SyncObject $message - * @param array $mapping - * - * @access private - * @return - */ - private function setPropsInMAPI($mapimessage, $message, $mapping) { - $mapiprops = $this->getPropIdsFromStrings($mapping); - $unsetVars = $message->getUnsetVars(); - $propsToDelete = array(); - $propsToSet = array(); - - foreach ($mapiprops as $asprop => $mapiprop) { - if(isset($message->$asprop)) { - - // UTF8->windows1252.. this is ok for all numerical values - if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) { - if(is_array($message->$asprop)) - $value = array_map("u2wi", $message->$asprop); - else - $value = u2wi($message->$asprop); - } else { - $value = $message->$asprop; - } - - // Make sure the php values are the correct type - switch(mapi_prop_type($mapiprop)) { - case PT_BINARY: - case PT_STRING8: - settype($value, "string"); - break; - case PT_BOOLEAN: - settype($value, "boolean"); - break; - case PT_SYSTIME: - case PT_LONG: - settype($value, "integer"); - break; - } - - // decode base64 value - if($mapiprop == PR_RTF_COMPRESSED) { - $value = base64_decode($value); - if(strlen($value) == 0) - continue; // PDA will sometimes give us an empty RTF, which we'll ignore. - - // Note that you can still remove notes because when you remove notes it gives - // a valid compressed RTF with nothing in it. - - } - // if an "empty array" is to be saved, it the mvprop should be deleted - fixes Mantis #468 - if (is_array($value) && empty($value)) { - $propsToDelete[] = $mapiprop; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->setPropsInMAPI(): Property '%s' to be deleted as it is an empty array", $asprop)); - } - else { - // all properties will be set at once - $propsToSet[$mapiprop] = $value; - } - } - elseif (in_array($asprop, $unsetVars)) { - $propsToDelete[] = $mapiprop; - } - } - - mapi_setprops($mapimessage, $propsToSet); - if (mapi_last_hresult()) { - Zlog::Write(LOGLEVEL_WARN, sprintf("Failed to set properties, trying to set them separately. Error code was:%x", mapi_last_hresult())); - $this->setPropsIndividually($mapimessage, $propsToSet, $mapiprops); - } - - mapi_deleteprops($mapimessage, $propsToDelete); - - //clean up - unset($unsetVars, $propsToDelete); - } - - /** - * Sets the properties one by one in a MAPI object - * - * @param mixed &$mapimessage - * @param array &$propsToSet - * @param array &$mapiprops - * - * @access private - * @return - */ - private function setPropsIndividually(&$mapimessage, &$propsToSet, &$mapiprops) { - foreach ($propsToSet as $prop => $value) { - mapi_setprops($mapimessage, array($prop => $value)); - if (mapi_last_hresult()) { - Zlog::Write(LOGLEVEL_ERROR, sprintf("Failed setting property [%s] with value [%s], error code was:%x", array_search($prop, $mapiprops), $value, mapi_last_hresult())); - } - } - - } - - /** - * Gets the properties from a MAPI object and sets them in the Sync object according to mapping - * - * @param SyncObject &$message - * @param mixed $mapimessage - * @param array $mapping - * - * @access private - * @return - */ - private function getPropsFromMAPI(&$message, $mapimessage, $mapping) { - $messageprops = $this->getProps($mapimessage, $mapping); - foreach ($mapping as $asprop => $mapiprop) { - // Get long strings via openproperty - if (isset($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) { - if ($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT || - $messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) { - $messageprops[$mapiprop] = MAPIUtils::readPropStream($mapimessage, $mapiprop); - } - } - - if(isset($messageprops[$mapiprop])) { - if(mapi_prop_type($mapiprop) == PT_BOOLEAN) { - // Force to actual '0' or '1' - if($messageprops[$mapiprop]) - $message->$asprop = 1; - else - $message->$asprop = 0; - } else { - // Special handling for PR_MESSAGE_FLAGS - if($mapiprop == PR_MESSAGE_FLAGS) - $message->$asprop = $messageprops[$mapiprop] & 1; // only look at 'read' flag - else if($mapiprop == PR_RTF_COMPRESSED) - //do not send rtf to the mobile - continue; - else if(is_array($messageprops[$mapiprop])) - $message->$asprop = array_map("w2u", $messageprops[$mapiprop]); - else { - if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) - $message->$asprop = w2u($messageprops[$mapiprop]); - else - $message->$asprop = $messageprops[$mapiprop]; - } - } - } - } - } - - /** - * Wraps getPropIdsFromStrings() calls - * - * @param mixed &$mapiprops - * - * @access private - * @return - */ - private function getPropIdsFromStrings(&$mapiprops) { - return getPropIdsFromStrings($this->store, $mapiprops); - } - - /** - * Wraps mapi_getprops() calls - * - * @param mixed &$mapiprops - * - * @access private - * @return - */ - protected function getProps($mapimessage, &$mapiproperties) { - $mapiproperties = $this->getPropIdsFromStrings($mapiproperties); - return mapi_getprops($mapimessage, $mapiproperties); - } - - /** - * Returns an GMT timezone array - * - * @access private - * @return array - */ - private function getGMTTZ() { - $tz = array( - "bias" => 0, - "tzname" => "", - "dstendyear" => 0, - "dstendmonth" => 10, - "dstendday" => 0, - "dstendweek" => 5, - "dstendhour" => 2, - "dstendminute" => 0, - "dstendsecond" => 0, - "dstendmillis" => 0, - "stdbias" => 0, - "tznamedst" => "", - "dststartyear" => 0, - "dststartmonth" => 3, - "dststartday" => 0, - "dststartweek" => 5, - "dststarthour" => 1, - "dststartminute" => 0, - "dststartsecond" => 0, - "dststartmillis" => 0, - "dstbias" => -60 - ); - - return $tz; - } - - /** - * Unpack timezone info from MAPI - * - * @param string $data - * - * @access private - * @return array - */ - private function getTZFromMAPIBlob($data) { - $unpacked = unpack("lbias/lstdbias/ldstbias/" . - "vconst1/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" . - "vconst2/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis", $data); - return $unpacked; - } - - /** - * Unpack timezone info from Sync - * - * @param string $data - * - * @access private - * @return array - */ - private function getTZFromSyncBlob($data) { - $tz = unpack( "lbias/a64tzname/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" . - "lstdbias/a64tznamedst/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis/" . - "ldstbias", $data); - - // Make the structure compatible with class.recurrence.php - $tz["timezone"] = $tz["bias"]; - $tz["timezonedst"] = $tz["dstbias"]; - - return $tz; - } - - /** - * Pack timezone info for MAPI - * - * @param array $tz - * - * @access private - * @return string - */ - private function getMAPIBlobFromTZ($tz) { - $packed = pack("lll" . "vvvvvvvvv" . "vvvvvvvvv", - $tz["bias"], $tz["stdbias"], $tz["dstbias"], - 0, 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"], - 0, 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"]); - - return $packed; - } - - /** - * Checks the date to see if it is in DST, and returns correct GMT date accordingly - * - * @param long $localtime - * @param array $tz - * - * @access private - * @return long - */ - private function getGMTTimeByTZ($localtime, $tz) { - if(!isset($tz) || !is_array($tz)) - return $localtime; - - if($this->isDST($localtime, $tz)) - return $localtime + $tz["bias"]*60 + $tz["dstbias"]*60; - else - return $localtime + $tz["bias"]*60; - } - - /** - * Returns the local time for the given GMT time, taking account of the given timezone - * - * @param long $gmttime - * @param array $tz - * - * @access private - * @return long - */ - private function getLocaltimeByTZ($gmttime, $tz) { - if(!isset($tz) || !is_array($tz)) - return $gmttime; - - if($this->isDST($gmttime - $tz["bias"]*60, $tz)) // may bug around the switch time because it may have to be 'gmttime - bias - dstbias' - return $gmttime - $tz["bias"]*60 - $tz["dstbias"]*60; - else - return $gmttime - $tz["bias"]*60; - } - - /** - * Returns TRUE if it is the summer and therefore DST is in effect - * - * @param long $localtime - * @param array $tz - * - * @access private - * @return boolean - */ - private function isDST($localtime, $tz) { - if( !isset($tz) || !is_array($tz) || - !isset($tz["dstbias"]) || $tz["dstbias"] == 0 || - !isset($tz["dststartmonth"]) || $tz["dststartmonth"] == 0 || - !isset($tz["dstendmonth"]) || $tz["dstendmonth"] == 0) - return false; - - $year = gmdate("Y", $localtime); - $start = $this->getTimestampOfWeek($year, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststartday"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"]); - $end = $this->getTimestampOfWeek($year, $tz["dstendmonth"], $tz["dstendweek"], $tz["dstendday"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"]); - - if($start < $end) { - // northern hemisphere (july = dst) - if($localtime >= $start && $localtime < $end) - $dst = true; - else - $dst = false; - } else { - // southern hemisphere (january = dst) - if($localtime >= $end && $localtime < $start) - $dst = false; - else - $dst = true; - } - - return $dst; - } - - /** - * Returns the local timestamp for the $week'th $wday of $month in $year at $hour:$minute:$second - * - * @param int $year - * @param int $month - * @param int $week - * @param int $wday - * @param int $hour - * @param int $minute - * @param int $second - * - * @access private - * @return long - */ - private function getTimestampOfWeek($year, $month, $week, $wday, $hour, $minute, $second) { - if ($month == 0) - return; - - $date = gmmktime($hour, $minute, $second, $month, 1, $year); - - // Find first day in month which matches day of the week - while(1) { - $wdaynow = gmdate("w", $date); - if($wdaynow == $wday) - break; - $date += 24 * 60 * 60; - } - - // Forward $week weeks (may 'overflow' into the next month) - $date = $date + $week * (24 * 60 * 60 * 7); - - // Reverse 'overflow'. Eg week '10' will always be the last week of the month in which the - // specified weekday exists - while(1) { - $monthnow = gmdate("n", $date); // gmdate returns 1-12 - if($monthnow > $month) - $date = $date - (24 * 7 * 60 * 60); - else - break; - } - - return $date; - } - - /** - * Normalize the given timestamp to the start of the day - * - * @param long $timestamp - * - * @access private - * @return long - */ - private function getDayStartOfTimestamp($timestamp) { - return $timestamp - ($timestamp % (60 * 60 * 24)); - } - - /** - * Returns an SMTP address from an entry id - * - * @param string $entryid - * - * @access private - * @return string - */ - private function getSMTPAddressFromEntryID($entryid) { - $addrbook = $this->getAddressbook(); - - $mailuser = mapi_ab_openentry($addrbook, $entryid); - if(!$mailuser) - return ""; - - $props = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS)); - - $addrtype = isset($props[PR_ADDRTYPE]) ? $props[PR_ADDRTYPE] : ""; - - if(isset($props[PR_SMTP_ADDRESS])) - return $props[PR_SMTP_ADDRESS]; - - if($addrtype == "SMTP" && isset($props[PR_EMAIL_ADDRESS])) - return $props[PR_EMAIL_ADDRESS]; - elseif ($addrtype == "ZARAFA" && isset($props[PR_EMAIL_ADDRESS])) { - $userinfo = mapi_zarafa_getuser_by_name($this->store, $props[PR_EMAIL_ADDRESS]); - if (is_array($userinfo) && isset($userinfo["emailaddress"])) - return $userinfo["emailaddress"]; - } - - return ""; - } - - /** - * Returns fullname from an entryid - * - * @param binary $entryid - * @return string fullname or false on error - */ - private function getFullnameFromEntryID($entryid) { - $addrbook = $this->getAddressbook(); - $mailuser = mapi_ab_openentry($addrbook, $entryid); - if(!$mailuser) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to get mailuser for getFullnameFromEntryID (0x%X)", mapi_last_hresult())); - return false; - } - - $props = mapi_getprops($mailuser, array(PR_DISPLAY_NAME)); - if (isset($props[PR_DISPLAY_NAME])) { - return $props[PR_DISPLAY_NAME]; - } - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to get fullname for getFullnameFromEntryID (0x%X)", mapi_last_hresult())); - return false; - } - - /** - * Builds a displayname from several separated values - * - * @param SyncContact $contact - * - * @access private - * @return string - */ - private function composeDisplayName(&$contact) { - // Set display name and subject to a combined value of firstname and lastname - $cname = (isset($contact->prefix))?u2w($contact->prefix)." ":""; - $cname .= u2w($contact->firstname); - $cname .= (isset($contact->middlename))?" ". u2w($contact->middlename):""; - $cname .= " ". u2w($contact->lastname); - $cname .= (isset($contact->suffix))?" ". u2w($contact->suffix):""; - return trim($cname); - } - - /** - * Sets all dependent properties for an email address - * - * @param string $emailAddress - * @param string $displayName - * @param int $cnt - * @param array &$props - * @param array &$properties - * @param array &$nremails - * @param int &$abprovidertype - * - * @access private - * @return - */ - private function setEmailAddress($emailAddress, $displayName, $cnt, &$props, &$properties, &$nremails, &$abprovidertype){ - if (isset($emailAddress)) { - $name = (isset($displayName)) ? $displayName : $emailAddress; - - $props[$properties["emailaddress$cnt"]] = $emailAddress; - $props[$properties["emailaddressdemail$cnt"]] = $emailAddress; - $props[$properties["emailaddressdname$cnt"]] = $name; - $props[$properties["emailaddresstype$cnt"]] = "SMTP"; - $props[$properties["emailaddressentryid$cnt"]] = mapi_createoneoff($name, "SMTP", $emailAddress); - $nremails[] = $cnt - 1; - $abprovidertype |= 2 ^ ($cnt - 1); - } - } - - /** - * Sets the properties for an address string - * - * @param string $type which address is being set - * @param string $city - * @param string $country - * @param string $postalcode - * @param string $state - * @param string $street - * @param array &$props - * @param array &$properties - * - * @access private - * @return - */ - private function setAddress($type, &$city, &$country, &$postalcode, &$state, &$street, &$props, &$properties) { - if (isset($city)) $props[$properties[$type."city"]] = $city = u2w($city); - - if (isset($country)) $props[$properties[$type."country"]] = $country = u2w($country); - - if (isset($postalcode)) $props[$properties[$type."postalcode"]] = $postalcode = u2w($postalcode); - - if (isset($state)) $props[$properties[$type."state"]] = $state = u2w($state); - - if (isset($street)) $props[$properties[$type."street"]] = $street = u2w($street); - - //set composed address - $address = Utils::BuildAddressString($street, $postalcode, $city, $state, $country); - if ($address) $props[$properties[$type."address"]] = $address; - } - - /** - * Sets the properties for a mailing address - * - * @param string $city - * @param string $country - * @param string $postalcode - * @param string $state - * @param string $street - * @param string $address - * @param array &$props - * @param array &$properties - * - * @access private - * @return - */ - private function setMailingAddress($city, $country, $postalcode, $state, $street, $address, &$props, &$properties) { - if (isset($city)) $props[$properties["city"]] = $city; - if (isset($country)) $props[$properties["country"]] = $country; - if (isset($postalcode)) $props[$properties["postalcode"]] = $postalcode; - if (isset($state)) $props[$properties["state"]] = $state; - if (isset($street)) $props[$properties["street"]] = $street; - if (isset($address)) $props[$properties["postaladdress"]] = $address; - } - - /** - * Sets data in a recurrence array - * - * @param SyncObject $message - * @param array &$recur - * - * @access private - * @return - */ - private function setRecurrence($message, &$recur) { - if (isset($message->complete)) { - $recur["complete"] = $message->complete; - } - - if(!isset($message->recurrence->interval)) - $message->recurrence->interval = 1; - - //set the default value of numoccur - $recur["numoccur"] = 0; - //a place holder for recurrencetype property - $recur["recurrencetype"] = 0; - - switch($message->recurrence->type) { - case 0: - $recur["type"] = 10; - if(isset($message->recurrence->dayofweek)) - $recur["subtype"] = 1; - else - $recur["subtype"] = 0; - - $recur["everyn"] = $message->recurrence->interval * (60 * 24); - $recur["recurrencetype"] = 1; - break; - case 1: - $recur["type"] = 11; - $recur["subtype"] = 1; - $recur["everyn"] = $message->recurrence->interval; - $recur["recurrencetype"] = 2; - break; - case 2: - $recur["type"] = 12; - $recur["subtype"] = 2; - $recur["everyn"] = $message->recurrence->interval; - $recur["recurrencetype"] = 3; - break; - case 3: - $recur["type"] = 12; - $recur["subtype"] = 3; - $recur["everyn"] = $message->recurrence->interval; - $recur["recurrencetype"] = 3; - break; - case 4: - $recur["type"] = 13; - $recur["subtype"] = 1; - $recur["everyn"] = $message->recurrence->interval * 12; - $recur["recurrencetype"] = 4; - break; - case 5: - $recur["type"] = 13; - $recur["subtype"] = 2; - $recur["everyn"] = $message->recurrence->interval * 12; - $recur["recurrencetype"] = 4; - break; - case 6: - $recur["type"] = 13; - $recur["subtype"] = 3; - $recur["everyn"] = $message->recurrence->interval * 12; - $recur["recurrencetype"] = 4; - break; - } - - // "start" and "end" are in GMT when passing to class.recurrence - $recur["end"] = $this->getDayStartOfTimestamp(0x7fffffff); // Maximum GMT value for end by default - - if(isset($message->recurrence->until)) { - $recur["term"] = 0x21; - $recur["end"] = $message->recurrence->until; - } else if(isset($message->recurrence->occurrences)) { - $recur["term"] = 0x22; - $recur["numoccur"] = $message->recurrence->occurrences; - } else { - $recur["term"] = 0x23; - } - - if(isset($message->recurrence->dayofweek)) - $recur["weekdays"] = $message->recurrence->dayofweek; - if(isset($message->recurrence->weekofmonth)) - $recur["nday"] = $message->recurrence->weekofmonth; - if(isset($message->recurrence->monthofyear)) { - // MAPI stores months as the amount of minutes until the beginning of the month in a - // non-leapyear. Why this is, is totally unclear. - $monthminutes = array(0,44640,84960,129600,172800,217440,260640,305280,348480,393120,437760,480960); - $recur["month"] = $monthminutes[$message->recurrence->monthofyear-1]; - } - if(isset($message->recurrence->dayofmonth)) - $recur["monthday"] = $message->recurrence->dayofmonth; - } - - /** - * Extracts the email address (mailbox@host) from an email address because - * some devices send email address as "Firstname Lastname" - * - * @link http://developer.berlios.de/mantis/view.php?id=486 - * - * @param string $email - * - * @access private - * @return string or false on error - */ - private function extractEmailAddress($email) { - if (!isset($this->zRFC822)) $this->zRFC822 = new Mail_RFC822(); - $parsedAddress = $this->zRFC822->parseAddressList($email); - if (!isset($parsedAddress[0]->mailbox) || !isset($parsedAddress[0]->host)) return false; - - return $parsedAddress[0]->mailbox.'@'.$parsedAddress[0]->host; - } - - /** - * Returns the message body for a required format - * - * @param MAPIMessage $mapimessage - * @param int $bpReturnType - * @param SyncObject $message - * - * @access private - * @return boolean - */ - private function setMessageBodyForType($mapimessage, $bpReturnType, &$message) { - //default value is PR_BODY - $property = PR_BODY; - switch ($bpReturnType) { - case SYNC_BODYPREFERENCE_HTML: - $property = PR_HTML; - break; - case SYNC_BODYPREFERENCE_RTF: - $property = PR_RTF_COMPRESSED; - break; - case SYNC_BODYPREFERENCE_MIME: - $stat = $this->imtoinet($mapimessage, $message); - if (isset($message->asbody)) - $message->asbody->type = $bpReturnType; - return $stat; - } - - $body = mapi_message_openproperty($mapimessage, $property); - //set the properties according to supported AS version - if (Request::GetProtocolVersion() >= 12.0) { - $message->asbody = new SyncBaseBody(); - $message->asbody->type = $bpReturnType; - if ($bpReturnType == SYNC_BODYPREFERENCE_RTF) - $message->asbody->data = base64_encode($body); - elseif (isset($message->internetcpid) && $bpReturnType == SYNC_BODYPREFERENCE_HTML) - $message->asbody->data = Utils::ConvertCodepageStringToUtf8($message->internetcpid, $body); - else - $message->asbody->data = w2u($body); - $message->asbody->estimatedDataSize = strlen($message->asbody->data); - } - else { - $message->body = str_replace("\n","\r\n", w2u(str_replace("\r", "", $body))); - $message->bodysize = strlen($message->body); - $message->bodytruncated = 0; - } - - return true; - } - - /** - * A wrapper for mapi_inetmapi_imtoinet function - * - * @param MAPIMessage $mapimessage - * @param SyncObject $message - * - * @access private - * @return boolean - */ - private function imtoinet($mapimessage, &$message) { - // if it is a signed message get a full attachment generated by ZCP - $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS)); - if (isset($props[PR_MESSAGE_CLASS]) && $props[PR_MESSAGE_CLASS] && strpos(strtolower($props[PR_MESSAGE_CLASS]), 'multipartsigned')) { - // find the required attachment - $attachtable = mapi_message_getattachmenttable($mapimessage); - mapi_table_restrict($attachtable, MAPIUtils::GetSignedAttachmentRestriction()); - if (mapi_table_getrowcount($attachtable) == 1) { - $rows = mapi_table_queryrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE), 0, 1); - if (isset($rows[0][PR_ATTACH_NUM])) { - $mapiattach = mapi_message_openattach($mapimessage, $rows[0][PR_ATTACH_NUM]); - $stream = mapi_openpropertytostream($mapiattach, PR_ATTACH_DATA_BIN); - $streamsize = $rows[0][PR_ATTACH_SIZE]; - } - } - } - elseif (function_exists("mapi_inetmapi_imtoinet")) { - $addrbook = $this->getAddressbook(); - $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1)); - $mstreamstat = mapi_stream_stat($stream); - $streamsize = $mstreamstat["cb"]; - } - - if (isset($stream) && isset($streamsize)) { - if (Request::GetProtocolVersion() >= 12.0) { - if (!isset($message->asbody)) - $message->asbody = new SyncBaseBody(); - //TODO data should be wrapped in a MapiStreamWrapper - $message->asbody->data = mapi_stream_read($stream, $streamsize); - $message->asbody->estimatedDataSize = $streamsize; - $message->asbody->truncated = 0; - } - else { - $message->mimetruncated = 0; - //TODO mimedata should be a wrapped in a MapiStreamWrapper - $message->mimedata = mapi_stream_read($stream, $streamsize); - $message->mimesize = $streamsize; - } - unset($message->body, $message->bodytruncated); - return true; - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Error opening attachment for imtoinet")); - } - return false; - } - - /** - * Sets the message body - * - * @param MAPIMessage $mapimessage - * @param ContentParameters $contentparameters - * @param SyncObject $message - */ - private function setMessageBody($mapimessage, $contentparameters, &$message) { - //get the available body preference types - $bpTypes = $contentparameters->GetBodyPreference(); - if ($bpTypes !== false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BodyPreference types: %s", implode(', ', $bpTypes))); - //do not send mime data if the client requests it - if (($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER) && ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes)!== false)) { - unset($bpTypes[$key]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Remove mime body preference type because the device required no mime support. BodyPreference types: %s", implode(', ', $bpTypes))); - } - //get the best fitting preference type - $bpReturnType = Utils::GetBodyPreferenceBestMatch($bpTypes); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetBodyPreferenceBestMatch: %d", $bpReturnType)); - $bpo = $contentparameters->BodyPreference($bpReturnType); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview())); - - $this->setMessageBodyForType($mapimessage, $bpReturnType, $message); - //only set the truncation size data if device set it in request - if ( $bpo->GetTruncationSize() != false && - $bpReturnType != SYNC_BODYPREFERENCE_MIME && - $message->asbody->estimatedDataSize > $bpo->GetTruncationSize() && - $contentparameters->GetTruncation() != SYNC_TRUNCATION_ALL // do not truncate message if the whole is requested, e.g. on fetch - ) { - $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $bpo->GetTruncationSize()); - $message->asbody->truncated = 1; - - } - // set the preview or windows phones won't show the preview of an email - if (Request::GetProtocolVersion() >= 14.0 && $bpo->GetPreview()) { - $message->asbody->preview = Utils::Utf8_truncate(MAPIUtils::readPropStream($mapimessage, PR_BODY), $bpo->GetPreview()); - } - } - else { - // Override 'body' for truncation - $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation()); - $this->setMessageBodyForType($mapimessage, SYNC_BODYPREFERENCE_PLAIN, $message); - - if($message->bodysize > $truncsize) { - $message->body = Utils::Utf8_truncate($message->body, $truncsize); - $message->bodytruncated = 1; - } - - if (!isset($message->body) || strlen($message->body) == 0) - $message->body = " "; - - if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_ALWAYS) { - //set the html body for iphone in AS 2.5 version - $this->imtoinet($mapimessage, $message); - } - } - } - - /** - * Calculates the native body type of a message using available properties. Refer to oxbbody - * - * @param array $messageprops - * - * @access private - * @return int - */ - private function getNativeBodyType($messageprops) { - //check if the properties are set and get the error code if needed - if (!isset($messageprops[PR_BODY])) $messageprops[PR_BODY] = $this->getError(PR_BODY, $messageprops); - if (!isset($messageprops[PR_RTF_COMPRESSED])) $messageprops[PR_RTF_COMPRESSED] = $this->getError(PR_RTF_COMPRESSED, $messageprops); - if (!isset($messageprops[PR_HTML])) $messageprops[PR_HTML] = $this->getError(PR_HTML, $messageprops); - if (!isset($messageprops[PR_RTF_IN_SYNC])) $messageprops[PR_RTF_IN_SYNC] = $this->getError(PR_RTF_IN_SYNC, $messageprops); - - if ( // 1 - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 2 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 3 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 4 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 5 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_HTML; - elseif ( // 6 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 7 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_HTML; - elseif ( // 8 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 9.1 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 9.2 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 9.3 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 9.4 - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_HTML; - else // 10 - return SYNC_BODYPREFERENCE_PLAIN; - } - - /** - * Returns the error code for a given property. Helper for getNativeBodyType function. - * - * @param int $tag - * @param array $messageprops - * - * @access private - * @return int (MAPI_ERROR_CODE) - */ - private function getError($tag, $messageprops) { - $prBodyError = mapi_prop_tag(PT_ERROR, mapi_prop_id($tag)); - if(isset($messageprops[$prBodyError]) && mapi_is_error($messageprops[$prBodyError])) { - if($messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT || - $messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) { - return MAPI_E_NOT_ENOUGH_MEMORY; - } - } - return MAPI_E_NOT_FOUND; - } - - /** - * Sets properties for an email message - * - * @param mixed $mapimessage - * @param SyncMail $message - * - * @access private - * @return void - */ - private function setFlag($mapimessage, &$message){ - // do nothing if protocoll version is lower than 12.0 as flags haven't been defined before - if (Request::GetProtocolVersion() < 12.0 ) return; - - $message->flag = new SyncMailFlags(); - - $this->getPropsFromMAPI($message->flag, $mapimessage, MAPIMapping::GetMailFlagsMapping()); - } - - /** - * Sets information from SyncBaseBody type for a MAPI message. - * - * @param SyncBaseBody $asbody - * @param array $props - * @param array $appointmentprops - * - * @access private - * @return void - */ - private function setASbody($asbody, &$props, $appointmentprops) { - if (isset($asbody->type) && isset($asbody->data) && strlen($asbody->data) > 0) { - switch ($asbody->type) { - case SYNC_BODYPREFERENCE_PLAIN: - default: - //set plain body if the type is not in valid range - $props[$appointmentprops["body"]] = u2w($asbody->data); - break; - case SYNC_BODYPREFERENCE_HTML: - $props[$appointmentprops["html"]] = u2w($asbody->data); - break; - case SYNC_BODYPREFERENCE_RTF: - break; - case SYNC_BODYPREFERENCE_MIME: - break; - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->setASbody either type or data are not set. Setting to empty body"); - $props[$appointmentprops["body"]] = ""; - } - } - - /** - * Get MAPI addressbook object - * - * @access private - * @return MAPIAddressbook object to be used with mapi_ab_* or false on failure - */ - private function getAddressbook() { - if (isset($this->addressbook) && $this->addressbook) { - return $this->addressbook; - } - $this->addressbook = mapi_openaddressbook($this->session); - $result = mapi_last_hresult(); - if ($result && $this->addressbook === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("MAPIProvider->getAddressbook error opening addressbook 0x%X", $result)); - return false; - } - return $this->addressbook; - } - - /** - * Gets the required store properties. - * - * @access private - * @return array - */ - private function getStoreProps() { - if (!isset($this->storeProps) || empty($this->storeProps)) { - ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getStoreProps(): Getting store properties."); - $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)); - } - return $this->storeProps; - } - - /** - * Gets the required inbox properties. - * - * @access private - * @return array - */ - private function getInboxProps() { - if (!isset($this->inboxProps) || empty($this->inboxProps)) { - ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->getInboxProps(): Getting inbox properties."); - $inbox = mapi_msgstore_getreceivefolder($this->store); - $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)); - } - return $this->inboxProps; - } -} diff --git a/sources/backend/zarafa/mapistreamwrapper.php b/sources/backend/zarafa/mapistreamwrapper.php deleted file mode 100644 index 3fb5cdf..0000000 --- a/sources/backend/zarafa/mapistreamwrapper.php +++ /dev/null @@ -1,146 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class MAPIStreamWrapper { - const PROTOCOL = "mapistream"; - - private $mapistream; - private $position; - private $streamlength; - - /** - * Opens the stream - * The mapistream reference is passed over the context - * - * @param string $path Specifies the URL that was passed to the original function - * @param string $mode The mode used to open the file, as detailed for fopen() - * @param int $options Holds additional flags set by the streams API - * @param string $opened_path If the path is opened successfully, and STREAM_USE_PATH is set in options, - * opened_path should be set to the full path of the file/resource that was actually opened. - * - * @access public - * @return boolean - */ - public function stream_open($path, $mode, $options, &$opened_path) { - $contextOptions = stream_context_get_options($this->context); - if (!isset($contextOptions[self::PROTOCOL]['stream'])) - return false; - - $this->position = 0; - - // this is our stream! - $this->mapistream = $contextOptions[self::PROTOCOL]['stream']; - - // get the data length from mapi - $stat = mapi_stream_stat($this->mapistream); - $this->streamlength = $stat["cb"]; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIStreamWrapper::stream_open(): initialized mapistream: %s streamlength: %d", $this->mapistream, $this->streamlength)); - - return true; - } - - /** - * Reads from stream - * - * @param int $len amount of bytes to be read - * - * @access public - * @return string - */ - public function stream_read($len) { - $len = ($this->position + $len > $this->streamlength) ? ($this->streamlength - $this->position) : $len; - $data = mapi_stream_read($this->mapistream, $len); - $this->position += strlen($data); - return $data; - } - - /** - * Returns the current position on stream - * - * @access public - * @return int - */ - public function stream_tell() { - return $this->position; - } - - /** - * Indicates if 'end of file' is reached - * - * @access public - * @return boolean - */ - public function stream_eof() { - return ($this->position >= $this->streamlength); - } - - /** - * Retrieves information about a stream - * - * @access public - * @return array - */ - public function stream_stat() { - return array( - 7 => $this->streamlength, - 'size' => $this->streamlength, - ); - } - - /** - * Instantiates a MAPIStreamWrapper - * - * @param mapistream $mapistream The stream to be wrapped - * - * @access public - * @return MAPIStreamWrapper - */ - static public function Open($mapistream) { - $context = stream_context_create(array(self::PROTOCOL => array('stream' => &$mapistream))); - return fopen(self::PROTOCOL . "://",'r', false, $context); - } -} - -stream_wrapper_register(MAPIStreamWrapper::PROTOCOL, "MAPIStreamWrapper"); diff --git a/sources/backend/zarafa/mapiutils.php b/sources/backend/zarafa/mapiutils.php deleted file mode 100644 index 238bd3b..0000000 --- a/sources/backend/zarafa/mapiutils.php +++ /dev/null @@ -1,585 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -/** - * - * MAPI to AS mapping class - * - * - */ -class MAPIUtils { - - /** - * Create a MAPI restriction to use within an email folder which will - * return all messages since since $timestamp - * - * @param long $timestamp Timestamp since when to include messages - * - * @access public - * @return array - */ - public static function GetEmailRestriction($timestamp) { - // ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInEmailSyncInterval() also needs to be changed - $restriction = array ( RES_PROPERTY, - array ( RELOP => RELOP_GE, - ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, - VALUE => $timestamp - ) - ); - - return $restriction; - } - - - /** - * Create a MAPI restriction to use in the calendar which will - * return all future calendar items, plus those since $timestamp - * - * @param MAPIStore $store the MAPI store - * @param long $timestamp Timestamp since when to include messages - * - * @access public - * @return array - */ - //TODO getting named properties - public static function GetCalendarRestriction($store, $timestamp) { - // This is our viewing window - $start = $timestamp; - $end = 0x7fffffff; // infinite end - - $props = MAPIMapping::GetAppointmentProperties(); - $props = getPropIdsFromStrings($store, $props); - - // ATTENTION: ON CHANGING THIS RESTRICTION, MAPIUtils::IsInCalendarSyncInterval() also needs to be changed - $restriction = Array(RES_OR, - Array( - // OR - // item.end > window.start && item.start < window.end - Array(RES_AND, - Array( - Array(RES_PROPERTY, - Array(RELOP => RELOP_LE, - ULPROPTAG => $props["starttime"], - VALUE => $end - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_GE, - ULPROPTAG => $props["endtime"], - VALUE => $start - ) - ) - ) - ), - // OR - Array(RES_OR, - Array( - // OR - // (EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start) - Array(RES_AND, - Array( - Array(RES_EXIST, - Array(ULPROPTAG => $props["recurrenceend"], - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => $props["isrecurring"], - VALUE => true - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_GE, - ULPROPTAG => $props["recurrenceend"], - VALUE => $start - ) - ) - ) - ), - // OR - // (!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end) - Array(RES_AND, - Array( - Array(RES_NOT, - Array( - Array(RES_EXIST, - Array(ULPROPTAG => $props["recurrenceend"] - ) - ) - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_LE, - ULPROPTAG => $props["starttime"], - VALUE => $end - ) - ), - Array(RES_PROPERTY, - Array(RELOP => RELOP_EQ, - ULPROPTAG => $props["isrecurring"], - VALUE => true - ) - ) - ) - ) - ) - ) // EXISTS OR - ) - ); // global OR - - return $restriction; - } - - - /** - * Create a MAPI restriction in order to check if a contact has a picture - * - * @access public - * @return array - */ - public static function GetContactPicRestriction() { - return array ( RES_PROPERTY, - array ( - RELOP => RELOP_EQ, - ULPROPTAG => mapi_prop_tag(PT_BOOLEAN, 0x7FFF), - VALUE => true - ) - ); - } - - - /** - * Create a MAPI restriction for search - * - * @access public - * - * @param string $query - * @return array - */ - public static function GetSearchRestriction($query) { - return array(RES_AND, - array( - array(RES_OR, - array( - array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_DISPLAY_NAME, VALUE => $query)), - array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_ACCOUNT, VALUE => $query)), - array(RES_CONTENT, array(FUZZYLEVEL => FL_SUBSTRING | FL_IGNORECASE, ULPROPTAG => PR_SMTP_ADDRESS, VALUE => $query)), - ), // RES_OR - ), - array(RES_OR, - array ( - array( - RES_PROPERTY, - array(RELOP => RELOP_EQ, ULPROPTAG => PR_OBJECT_TYPE, VALUE => MAPI_MAILUSER) - ), - array( - RES_PROPERTY, - array(RELOP => RELOP_EQ, ULPROPTAG => PR_OBJECT_TYPE, VALUE => MAPI_DISTLIST) - ) - ) - ) // RES_OR - ) // RES_AND - ); - } - - /** - * Create a MAPI restriction for a certain email address - * - * @access public - * - * @param MAPIStore $store the MAPI store - * @param string $query email address - * - * @return array - */ - public static function GetEmailAddressRestriction($store, $email) { - $props = MAPIMapping::GetContactProperties(); - $props = getPropIdsFromStrings($store, $props); - - return array(RES_OR, - array( - array( RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => $props['emailaddress1'], - VALUE => array($props['emailaddress1'] => $email), - ), - ), - array( RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => $props['emailaddress2'], - VALUE => array($props['emailaddress2'] => $email), - ), - ), - array( RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => $props['emailaddress3'], - VALUE => array($props['emailaddress3'] => $email), - ), - ), - ), - ); - } - - /** - * Create a MAPI restriction for a certain folder type - * - * @access public - * - * @param string $foldertype folder type for restriction - * @return array - */ - public static function GetFolderTypeRestriction($foldertype) { - return array( RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => PR_CONTAINER_CLASS, - VALUE => array(PR_CONTAINER_CLASS => $foldertype) - ), - ); - } - - /** - * Returns subfolders of given type for a folder or false if there are none. - * - * @access public - * - * @param MAPIFolder $folder - * @param string $type - * - * @return MAPITable|boolean - */ - public static function GetSubfoldersForType($folder, $type) { - $subfolders = mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH); - mapi_table_restrict($subfolders, MAPIUtils::GetFolderTypeRestriction($type)); - if (mapi_table_getrowcount($subfolders) > 0) { - return mapi_table_queryallrows($subfolders, array(PR_ENTRYID)); - } - return false; - } - - /** - * Checks if mapimessage is inside the synchronization interval - * also defined by MAPIUtils::GetEmailRestriction() - * - * @param MAPIStore $store mapi store - * @param MAPIMessage $mapimessage the mapi message to be checked - * @param long $timestamp the lower time limit - * - * @access public - * @return boolean - */ - public static function IsInEmailSyncInterval($store, $mapimessage, $timestamp) { - $p = mapi_getprops($mapimessage, array(PR_MESSAGE_DELIVERY_TIME)); - - if (isset($p[PR_MESSAGE_DELIVERY_TIME]) && $p[PR_MESSAGE_DELIVERY_TIME] >= $timestamp) { - ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInEmailSyncInterval: Message is in the synchronization interval"); - return true; - } - - ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInEmailSyncInterval: Message is OUTSIDE the synchronization interval"); - return false; - } - - /** - * Checks if mapimessage is inside the synchronization interval - * also defined by MAPIUtils::GetCalendarRestriction() - * - * @param MAPIStore $store mapi store - * @param MAPIMessage $mapimessage the mapi message to be checked - * @param long $timestamp the lower time limit - * - * @access public - * @return boolean - */ - public static function IsInCalendarSyncInterval($store, $mapimessage, $timestamp) { - // This is our viewing window - $start = $timestamp; - $end = 0x7fffffff; // infinite end - - $props = MAPIMapping::GetAppointmentProperties(); - $props = getPropIdsFromStrings($store, $props); - - $p = mapi_getprops($mapimessage, array($props["starttime"], $props["endtime"], $props["recurrenceend"], $props["isrecurring"], $props["recurrenceend"])); - - if ( - ( - isset($p[$props["endtime"]]) && isset($p[$props["starttime"]]) && - - //item.end > window.start && item.start < window.end - $p[$props["endtime"]] > $start && $p[$props["starttime"]] < $end - ) - || - ( - isset($p[$props["isrecurring"]]) && - - //(EXIST(recurrence_enddate_property) && item[isRecurring] == true && recurrence_enddate_property >= start) - isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["recurrenceend"]] >= $start - ) - || - ( - isset($p[$props["isrecurring"]]) && isset($p[$props["starttime"]]) && - - //(!EXIST(recurrence_enddate_property) && item[isRecurring] == true && item[start] <= end) - !isset($p[$props["recurrenceend"]]) && $p[$props["isrecurring"]] == true && $p[$props["starttime"]] <= $end - ) - ) { - ZLog::Write(LOGLEVEL_DEBUG, "MAPIUtils->IsInCalendarSyncInterval: Message is in the synchronization interval"); - return true; - } - - - ZLog::Write(LOGLEVEL_WARN, "MAPIUtils->IsInCalendarSyncInterval: Message is OUTSIDE the synchronization interval"); - return false; - } - - - /** - * Reads data of large properties from a stream - * - * @param MAPIMessage $message - * @param long $prop - * - * @access public - * @return string - */ - public static function readPropStream($message, $prop) { - $stream = mapi_openproperty($message, $prop, IID_IStream, 0, 0); - $ret = mapi_last_hresult(); - if ($ret == MAPI_E_NOT_FOUND) { - 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))); - return ""; - } - elseif ($ret) { - ZLog::Write(LOGLEVEL_ERROR, "MAPIUtils->readPropStream error opening stream: 0X%X", $ret); - return ""; - } - $data = ""; - $string = ""; - while(1) { - $data = mapi_stream_read($stream, 1024); - if(strlen($data) == 0) - break; - $string .= $data; - } - - return $string; - } - - - /** - * Checks if a store supports properties containing unicode characters - * - * @param MAPIStore $store - * - * @access public - * @return - */ - public static function IsUnicodeStore($store) { - $supportmask = mapi_getprops($store, array(PR_STORE_SUPPORT_MASK)); - if (isset($supportmask[PR_STORE_SUPPORT_MASK]) && ($supportmask[PR_STORE_SUPPORT_MASK] & STORE_UNICODE_OK)) { - ZLog::Write(LOGLEVEL_DEBUG, "Store supports properties containing Unicode characters."); - define('STORE_SUPPORTS_UNICODE', true); - //setlocale to UTF-8 in order to support properties containing Unicode characters - setlocale(LC_CTYPE, "en_US.UTF-8"); - define('STORE_INTERNET_CPID', INTERNET_CPID_UTF8); - } - } - - /** - * Returns the MAPI PR_CONTAINER_CLASS string for an ActiveSync Foldertype - * - * @param int $foldertype - * - * @access public - * @return string - */ - public static function GetContainerClassFromFolderType($foldertype) { - switch ($foldertype) { - case SYNC_FOLDER_TYPE_TASK: - case SYNC_FOLDER_TYPE_USER_TASK: - return "IPF.Task"; - break; - - case SYNC_FOLDER_TYPE_APPOINTMENT: - case SYNC_FOLDER_TYPE_USER_APPOINTMENT: - return "IPF.Appointment"; - break; - - case SYNC_FOLDER_TYPE_CONTACT: - case SYNC_FOLDER_TYPE_USER_CONTACT: - return "IPF.Contact"; - break; - - case SYNC_FOLDER_TYPE_NOTE: - case SYNC_FOLDER_TYPE_USER_NOTE: - return "IPF.StickyNote"; - break; - - case SYNC_FOLDER_TYPE_JOURNAL: - case SYNC_FOLDER_TYPE_USER_JOURNAL: - return "IPF.Journal"; - break; - - case SYNC_FOLDER_TYPE_INBOX: - case SYNC_FOLDER_TYPE_DRAFTS: - case SYNC_FOLDER_TYPE_WASTEBASKET: - case SYNC_FOLDER_TYPE_SENTMAIL: - case SYNC_FOLDER_TYPE_OUTBOX: - case SYNC_FOLDER_TYPE_USER_MAIL: - case SYNC_FOLDER_TYPE_OTHER: - case SYNC_FOLDER_TYPE_UNKNOWN: - default: - return "IPF.Note"; - break; - } - } - - public static function GetSignedAttachmentRestriction() { - return array( RES_PROPERTY, - array( RELOP => RELOP_EQ, - ULPROPTAG => PR_ATTACH_MIME_TAG, - VALUE => array(PR_ATTACH_MIME_TAG => 'multipart/signed') - ), - ); - } - - /** - * Calculates the native body type of a message using available properties. Refer to oxbbody. - * - * @param array $messageprops - * - * @access public - * @return int - */ - public static function GetNativeBodyType($messageprops) { - //check if the properties are set and get the error code if needed - if (!isset($messageprops[PR_BODY])) $messageprops[PR_BODY] = self::getError(PR_BODY, $messageprops); - if (!isset($messageprops[PR_RTF_COMPRESSED])) $messageprops[PR_RTF_COMPRESSED] = self::getError(PR_RTF_COMPRESSED, $messageprops); - if (!isset($messageprops[PR_HTML])) $messageprops[PR_HTML] = self::getError(PR_HTML, $messageprops); - if (!isset($messageprops[PR_RTF_IN_SYNC])) $messageprops[PR_RTF_IN_SYNC] = self::getError(PR_RTF_IN_SYNC, $messageprops); - - if ( // 1 - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 2 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 3 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 4 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 5 - ($messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_HTML; - elseif ( // 6 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 7 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_HTML; - elseif ( // 8 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 9.1 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - (!$messageprops[PR_RTF_IN_SYNC])) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 9.2 - ($messageprops[PR_RTF_COMPRESSED] != MAPI_E_NOT_FOUND || $messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_RTF; - elseif ( // 9.3 - ($messageprops[PR_BODY] != MAPI_E_NOT_FOUND || $messageprops[PR_BODY] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_HTML] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_PLAIN; - elseif ( // 9.4 - ($messageprops[PR_HTML] != MAPI_E_NOT_FOUND || $messageprops[PR_HTML] == MAPI_E_NOT_ENOUGH_MEMORY) && - ($messageprops[PR_BODY] == MAPI_E_NOT_FOUND) && - ($messageprops[PR_RTF_COMPRESSED] == MAPI_E_NOT_FOUND)) - return SYNC_BODYPREFERENCE_HTML; - else // 10 - return SYNC_BODYPREFERENCE_PLAIN; - } - - /** - * Returns the error code for a given property. Helper for getNativeBodyType function. - * - * @param int $tag - * @param array $messageprops - * - * @access private - * @return int (MAPI_ERROR_CODE) - */ - private static function getError($tag, $messageprops) { - $prBodyError = mapi_prop_tag(PT_ERROR, mapi_prop_id($tag)); - if(isset($messageprops[$prBodyError]) && mapi_is_error($messageprops[$prBodyError])) { - if($messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT || - $messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) { - return MAPI_E_NOT_ENOUGH_MEMORY; - } - } - return MAPI_E_NOT_FOUND; - } -} diff --git a/sources/backend/zarafa/tnefparser.php b/sources/backend/zarafa/tnefparser.php deleted file mode 100644 index 76fc966..0000000 --- a/sources/backend/zarafa/tnefparser.php +++ /dev/null @@ -1,720 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ -/** - * For more information on tnef refer to: - * http://msdn.microsoft.com/en-us/library/ms530652(EXCHG.10).aspx - * http://msdn.microsoft.com/en-us/library/cc425498(EXCHG.80).aspx - * - * The mapping between Microsoft Mail IPM classes and those used in - * MAPI see: http://msdn2.microsoft.com/en-us/library/ms527360.aspx - */ - -class TNEFParser { - const TNEF_SIGNATURE = 0x223e9f78; - const TNEF_LVL_MESSAGE = 0x01; - const TNEF_LVL_ATTACHMENT = 0x02; - const DWORD = 32; - const WORD = 16; - const BYTE = 8; - - /** - * Constructor - * We need a store in order to get the namedpropers - * - * @param mapistore $store - * @param array &$props properties to be set - * - * @access public - */ - public function TNEFParser(&$store, &$props) { - $this->store = $store; - $this->props = $props; - } - - /** - * Function reads tnef stream and puts mapi properties into an array. - * - * @param string $tnefstream - * @param array &$mapiprops mapi properties - * - * @access public - * @return int - */ - public function ExtractProps($tnefstream, &$mapiprops) { - $hresult = NOERROR; - $signature = 0; //tnef signature - 32 Bit - $key = 0; //a nonzero 16-bit unsigned integer - - $type = 0; // 32-bit value - $size = 0; // 32-bit value - $checksum = 0; //16-bit value - $component = 0; //8-bit value - either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT - $buffer = ""; - - //mapping between Microsoft Mail IPM classes and those in MAPI - $aClassMap = array( - "IPM.Microsoft Schedule.MtgReq" => "IPM.Schedule.Meeting.Request", - "IPM.Microsoft Schedule.MtgRespP" => "IPM.Schedule.Meeting.Resp.Pos", - "IPM.Microsoft Schedule.MtgRespN" => "IPM.Schedule.Meeting.Resp.Neg", - "IPM.Microsoft Schedule.MtgRespA" => "IPM.Schedule.Meeting.Resp.Tent", - "IPM.Microsoft Schedule.MtgCncl" => "IPM.Schedule.Meeting.Canceled", - "IPM.Microsoft Mail.Non-Delivery" => "Report.IPM.Note.NDR", - "IPM.Microsoft Mail.Read Receipt" => "Report.IPM.Note.IPNRN", - "IPM.Microsoft Mail.Note" => "IPM.Note", - "IPM.Microsoft Mail.Note" => "IPM", - ); - - //read signature - $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $signature); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: STREAM:".bin2hex($tnefstream)); - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef signature"); - return $hresult; - } - - //check signature - if ($signature != self::TNEF_SIGNATURE) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt signature."); - return MAPI_E_CORRUPT_DATA; - } - - //read key - $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $key); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading tnef key."); - return $hresult; - } - - // File is made of blocks, with each a type and size. Component and Key are ignored. - while(1) { - //the stream is empty. exit - if (strlen($tnefstream) == 0) return NOERROR; - - //read component - it is either self::TNEF_LVL_MESSAGE or self::TNEF_LVL_ATTACHMENT - $hresult = $this->readFromTnefStream($tnefstream, self::BYTE, $component); - if ($hresult !== NOERROR) { - $hresult = NOERROR; //EOF -> no error - return $hresult; - break; - } - - //read type - $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $type); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property type"); - return $hresult; - } - - //read size - $hresult = $this->readFromTnefStream($tnefstream, self::DWORD, $size); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property size"); - return $hresult; - } - - if ($size == 0) { - // do not allocate 0 size data block - ZLog::Write(LOGLEVEL_WARN, "TNEF: Size is 0. Corrupt data."); - return MAPI_E_CORRUPT_DATA; - } - - //read buffer - $hresult = $this->readBuffer($tnefstream, $size, $buffer); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - - //read checksum - $hresult = $this->readFromTnefStream($tnefstream, self::WORD, $checksum); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum."); - return $hresult; - } - - // Loop through all the blocks of the TNEF data. We are only interested - // in the properties block for now (0x00069003) - switch ($type) { - case 0x00069003: - $hresult = $this->readMapiProps($buffer, $size, $mapiprops); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi properties' part."); - return $hresult; - } - break; - case 0x00078008: // PR_MESSAGE_CLASS - $msMailClass = trim($buffer); - if (array_key_exists($msMailClass, $aClassMap)) { - $messageClass = $aClassMap[$msMailClass]; - } - else { - $messageClass = $msMailClass; - } - $mapiprops[PR_MESSAGE_CLASS] = $messageClass; - break; - case 0x00050008: // PR_OWNER_APPT_ID - $mapiprops[PR_OWNER_APPT_ID] = $buffer; - break; - case 0x00040009: // PR_RESPONSE_REQUESTED - $mapiprops[PR_RESPONSE_REQUESTED] = $buffer; - break; - - // --- TNEF attachemnts --- - case 0x00069002: - break; - case 0x00018010: // PR_ATTACH_FILENAME - break; - case 0x00068011: // PR_ATTACH_RENDERING, extra icon information - break; - case 0x0006800f: // PR_ATTACH_DATA_BIN, will be set via OpenProperty() in ECTNEF::Finish() - break; - case 0x00069005: // Attachment property stream - break; - default: - // Ignore this block - break; - } - } - return NOERROR; - } - - /** - * Reads a given number of bits from stream and converts them from little indian in a "normal" order. The function - * also cuts the tnef stream after reading. - * - * @param string &$tnefstream - * @param int $bits - * @param array &$element the read element - * - * @access private - * @return int - */ - private function readFromTnefStream(&$tnefstream, $bits, &$element) { - $bytes = $bits / 8; - - $part = substr($tnefstream, 0, $bytes); - $packs = array(); - - switch ($bits) { - case self::DWORD: - $packs = unpack("V", $part); - break; - case self::WORD: - $packs = unpack("v", $part); - break; - case self::BYTE: - $packs[1] = ord($part[0]); - break; - default: - $packs = array(); - break; - } - - if (empty($packs) || !isset($packs[1])) return MAPI_E_CORRUPT_DATA; - - $tnefstream = substr($tnefstream, $bytes); - $element = $packs[1]; - return NOERROR; - } - - /** - * Reads a given number of bytes from stream and puts them into $element. The function - * also cuts the tnef stream after reading. - * - * @param string &$tnefstream - * @param int $bytes - * @param array &$element the read element - * - * @access private - * @return int - */ - private function readBuffer(&$tnefstream, $bytes, &$element) { - $element = substr($tnefstream, 0, $bytes); - $tnefstream = substr($tnefstream, $bytes); - return NOERROR; - - } - - /** - * Reads mapi props from buffer into an anrray. - * - * @param string &$buffer - * @param int $size - * @param array &$mapiprops - * - * @access private - * @return int - */ - function readMapiProps(&$buffer, $size, &$mapiprops) { - $nrprops = 0; - //get number of mapi properties - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $nrprops); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error getting the number of mapi properties in stream."); - return $hresult; - } - - $size -= 4; - - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: nrprops:$nrprops"); - //loop through all the properties and add them to our internal list - while($nrprops) { - $hresult = $this->readSingleMapiProp($buffer, $size, $read, $mapiprops); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property."); - ZLog::Write(LOGLEVEL_WARN, "TNEF: result: " . sprintf("0x%X", $hresult)); - - return $hresult; - } - $nrprops--; - } - return NOERROR; - } - - /** - * Reads a single mapi prop. - * - * @param string &$buffer - * @param int $size - * @param mixed &$read - * @param array &$mapiprops - * - * @access private - * @return int - */ - private function readSingleMapiProp(&$buffer, &$size, &$read, &$mapiprops) { - $propTag = 0; - $len = 0; - $origSize = $size; - $isNamedId = 0; - $namedProp = 0; - $count = 0; - $mvProp = 0; - $guid = 0; - - if($size < 8) { - return MAPI_E_NOT_FOUND; - } - - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $propTag); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading a mapi property tag from the stream."); - return $hresult; - } - $size -= 4; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop type:".dechex(mapi_prop_type($propTag))); - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))); - if (mapi_prop_id($propTag) >= 0x8000) { - // Named property, first read GUID, then name/id - if($size < 24) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: Corrupt guid size for named property:".dechex($propTag)); - return MAPI_E_CORRUPT_DATA; - } - //strip GUID & name/id - $hresult = $this->readBuffer($buffer, 16, $guid); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - - $size -= 16; - //it is not used and is here only for eventual debugging - $readableGuid = unpack("VV/v2v/n4n", $guid); - $readableGuid = sprintf("{%08x-%04x-%04x-%04x-%04x%04x%04x}",$readableGuid['V'], $readableGuid['v1'], $readableGuid['v2'],$readableGuid['n1'],$readableGuid['n2'],$readableGuid['n3'],$readableGuid['n4']); - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: guid:$readableGuid"); - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $isNamedId); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property checksum."); - return $hresult; - } - $size -= 4; - - if($isNamedId != 0) { - // A string name follows - //read length of the property - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); - return $hresult; - } - $size -= 4; - if ($size < $len) { - return MAPI_E_CORRUPT_DATA; - } - //read the name of the property, eg Keywords - $hresult = $this->readBuffer($buffer, $len, $namedProp); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - - $size -= $len; - - //Re-align - $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0)); - $size -= $len & 3 ? 4 - ($len & 3) : 0; - } - else { - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $namedProp); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); - return $hresult; - } - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: named: 0x".sprintf("%04x", $namedProp)); - $size -= 4; - } - - if ($this->store !== false) { - $named = mapi_getidsfromnames($this->store, array($namedProp), array(makeguid($readableGuid))); - - $propTag = mapi_prop_tag(mapi_prop_type($propTag), mapi_prop_id($named[0])); - } - else { - ZLog::Write(LOGLEVEL_WARN, "TNEF: Store not available. It is impossible to get named properties"); - } - } - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: mapi prop tag: 0x".sprintf("%04x", mapi_prop_id($propTag))." ".sprintf("%04x", mapi_prop_type($propTag))); - if($propTag & MV_FLAG) { - if($size < 4) { - return MAPI_E_CORRUPT_DATA; - } - //read the number of properties - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $count); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading number of properties for:".dechex($propTag)); - return $hresult; - } - $size -= 4; - } - else { - $count = 1; - } - - for ($mvProp = 0; $mvProp < $count; $mvProp++) { - switch(mapi_prop_type($propTag) & ~MV_FLAG ) { - case PT_I2: - case PT_LONG: - $hresult = $this->readBuffer($buffer, 4, $value); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - $value = unpack("V", $value); - $value = intval($value[1], 16); - - if($propTag & MV_FLAG) { - $mapiprops[$propTag][] = $value; - } - else { - $mapiprops[$propTag] = $value; - } - $size -= 4; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: int or long propvalue:".$value); - break; - - case PT_R4: - if($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag][]); - - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - $size -= 4; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - case PT_BOOLEAN: - $hresult = $this->readBuffer($buffer, 4, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - $size -= 4; - //reported by dw2412 - //cast to integer as it evaluates to 1 or 0 because - //a non empty string evaluates to true :( - $mapiprops[$propTag] = (integer) bin2hex($mapiprops[$propTag]{0}); - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - - case PT_SYSTIME: - if($size < 8) { - return MAPI_E_CORRUPT_DATA; - } - if($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - //we have to convert the filetime to an unixtime timestamp - $filetime = unpack("V2v", $mapiprops[$propTag]); - //php on 64-bit systems converts unsigned values differently than on 32 bit systems - //we need this "fix" in order to get the same values on both types of systems - $filetime['v2'] = substr(sprintf("%08x",$filetime['v2']), -8); - $filetime['v1'] = substr(sprintf("%08x",$filetime['v1']), -8); - - $filetime = hexdec($filetime['v2'].$filetime['v1']); - $filetime = ($filetime - 116444736000000000) / 10000000; - $mapiprops[$propTag] = $filetime; - // we have to set the start and end times separately because the standard PR_START_DATE and PR_END_DATE aren't enough - if ($propTag == PR_START_DATE) { - $mapiprops[$this->props["starttime"]] = $mapiprops[$this->props["commonstart"]] = $filetime; - } - if ($propTag == PR_END_DATE) { - $mapiprops[$this->props["endtime"]] = $mapiprops[$this->props["commonend"]] = $filetime; - } - $size -= 8; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - case PT_DOUBLE: - case PT_CURRENCY: - case PT_I8: - case PT_APPTIME: - if($size < 8) { - return MAPI_E_CORRUPT_DATA; - } - if($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag][]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, 8, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - $size -= 8; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - case PT_STRING8: - if($size < 8) { - return MAPI_E_CORRUPT_DATA; - } - // Skip next 4 bytes, it's always '1' (ULONG) - $buffer = substr($buffer, 4); - $size -= 4; - - //read length of the property - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); - return $hresult; - } - $size -= 4; - if ($size < $len) { - return MAPI_E_CORRUPT_DATA; - } - - if ($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - //location fix. it looks like tnef uses this value for location - if (mapi_prop_id($propTag) == 0x8342) { - $mapiprops[$this->props["location"]] = $mapiprops[$propTag]; - unset($mapiprops[$propTag]); - } - - $size -= $len; - - //Re-align - $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0)); - $size -= $len & 3 ? 4 - ($len & 3) : 0; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - case PT_UNICODE: - if($size < 8) { - return MAPI_E_CORRUPT_DATA; - } - // Skip next 4 bytes, it's always '1' (ULONG) - $buffer = substr($buffer, 4); - $size -= 4; - - //read length of the property - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); - return $hresult; - } - $size -= 4; - if ($size < $len) { - return MAPI_E_CORRUPT_DATA; - } - //currently unicode strings are not supported bz mapi_setprops, so we'll use PT_STRING8 - $propTag = mapi_prop_tag(PT_STRING8, mapi_prop_id($propTag)); - - if ($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - - //location fix. it looks like tnef uses this value for location - if (mapi_prop_id($propTag) == 0x8342) { - $mapiprops[$this->props["location"]] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]); - unset($mapiprops[$propTag]); - } - - //convert from unicode to windows encoding - if (isset($mapiprops[$propTag])) $mapiprops[$propTag] = iconv("UCS-2","windows-1252", $mapiprops[$propTag]); - $size -= $len; - - //Re-align - $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0)); - $size -= $len & 3 ? 4 - ($len & 3) : 0; - if (isset($mapiprops[$propTag])) ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".$mapiprops[$propTag]); - break; - - case PT_OBJECT: // PST sends PT_OBJECT data. Treat as PT_BINARY - case PT_BINARY: - if($size < self::BYTE) { - return MAPI_E_CORRUPT_DATA; - } - // Skip next 4 bytes, it's always '1' (ULONG) - $buffer = substr($buffer, 4); - $size -= 4; - - //read length of the property - $hresult = $this->readFromTnefStream($buffer, self::DWORD, $len); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading mapi property's length"); - return $hresult; - } - $size -= 4; - - if (mapi_prop_type($propTag) == PT_OBJECT) { - // IMessage guid [ 0x00020307 C000 0000 0000 0000 00 00 00 46 ] - $buffer = substr($buffer, 16); - $size -= 16; - $len -= 16; - } - - if ($size < $len) { - return MAPI_E_CORRUPT_DATA; - } - - if ($propTag & MV_FLAG) { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag][]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - else { - $hresult = $this->readBuffer($buffer, $len, $mapiprops[$propTag]); - if ($hresult !== NOERROR) { - ZLog::Write(LOGLEVEL_WARN, "TNEF: There was an error reading stream property buffer"); - return $hresult; - } - } - - $size -= $len; - - //Re-align - $buffer = substr($buffer, ($len & 3 ? 4 - ($len & 3) : 0)); - $size -= $len & 3 ? 4 - ($len & 3) : 0; - ZLog::Write(LOGLEVEL_DEBUG, "TNEF: propvalue:".bin2hex($mapiprops[$propTag])); - break; - - default: - return MAPI_E_INVALID_PARAMETER; - break; - } - } - return NOERROR; - } -} diff --git a/sources/backend/zarafa/zarafa.php b/sources/backend/zarafa/zarafa.php deleted file mode 100644 index 72864bd..0000000 --- a/sources/backend/zarafa/zarafa.php +++ /dev/null @@ -1,1901 +0,0 @@ -. -* -* Consult LICENSE file for details -*************************************************/ - -// config file -require_once("backend/zarafa/config.php"); - -// include PHP-MAPI classes -include_once('backend/zarafa/mapi/mapi.util.php'); -include_once('backend/zarafa/mapi/mapidefs.php'); -include_once('backend/zarafa/mapi/mapitags.php'); -include_once('backend/zarafa/mapi/mapicode.php'); -include_once('backend/zarafa/mapi/mapiguid.php'); -include_once('backend/zarafa/mapi/class.baseexception.php'); -include_once('backend/zarafa/mapi/class.mapiexception.php'); -include_once('backend/zarafa/mapi/class.baserecurrence.php'); -include_once('backend/zarafa/mapi/class.taskrecurrence.php'); -include_once('backend/zarafa/mapi/class.recurrence.php'); -include_once('backend/zarafa/mapi/class.meetingrequest.php'); -include_once('backend/zarafa/mapi/class.freebusypublish.php'); - -// processing of RFC822 messages -require_once('include/z_RFC822.php'); - -// components of Zarafa backend -include_once('backend/zarafa/mapiutils.php'); -include_once('backend/zarafa/mapimapping.php'); -include_once('backend/zarafa/mapiprovider.php'); -include_once('backend/zarafa/mapiphpwrapper.php'); -include_once('backend/zarafa/mapistreamwrapper.php'); -include_once('backend/zarafa/importer.php'); -include_once('backend/zarafa/exporter.php'); - - -class BackendZarafa implements IBackend, ISearchProvider { - private $mainUser; - private $session; - private $defaultstore; - private $store; - private $storeName; - private $storeCache; - private $importedFolders; - private $notifications; - private $changesSink; - private $changesSinkFolders; - private $changesSinkStores; - private $wastebasket; - private $addressbook; - - // ZCP config parameter for PR_EC_ENABLED_FEATURES / PR_EC_DISABLED_FEATURES - const ZPUSH_ENABLED = 'mobile'; - - /** - * Constructor of the Zarafa Backend - * - * @access public - */ - public function BackendZarafa() { - $this->session = false; - $this->store = false; - $this->storeName = false; - $this->storeCache = array(); - $this->importedFolders = array(); - $this->notifications = false; - $this->changesSink = false; - $this->changesSinkFolders = array(); - $this->changesSinkStores = array(); - $this->wastebasket = false; - $this->session = false; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa using PHP-MAPI version: %s", phpversion("mapi"))); - } - - /** - * Indicates which StateMachine should be used - * - * @access public - * @return boolean ZarafaBackend uses the default FileStateMachine - */ - public function GetStateMachine() { - return false; - } - - /** - * Returns the ZarafaBackend as it implements the ISearchProvider interface - * This could be overwritten by the global configuration - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider() { - return $this; - } - - /** - * Indicates which AS version is supported by the backend. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_14; - } - - /** - * Authenticates the user with the configured Zarafa server - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - * @throws AuthenticationRequiredException - */ - public function Logon($user, $domain, $pass) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Logon(): Trying to authenticate user '%s'..", $user)); - $this->mainUser = strtolower($user); - - try { - // check if notifications are available in php-mapi - if(function_exists('mapi_feature') && mapi_feature('LOGONFLAGS')) { - // send Z-Push version and user agent to ZCP - ZP-589 - if (Utils::CheckMapiExtVersion('7.2.0')) { - $zpush_version = 'Z-Push_' . @constant('ZPUSH_VERSION'); - $user_agent = ZPush::GetDeviceManager()->GetUserAgent(); - $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0, $zpush_version, $user_agent); - } - else { - $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER, null, null, 0); - } - $this->notifications = true; - } - // old fashioned session - else { - $this->session = @mapi_logon_zarafa($user, $pass, MAPI_SERVER); - $this->notifications = false; - } - - if (mapi_last_hresult()) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZarafaBackend->Logon(): login failed with error code: 0x%X", mapi_last_hresult())); - if (mapi_last_hresult() == MAPI_E_NETWORK_ERROR) - throw new HTTPReturnCodeException("Error connecting to ZCP (login)", 503, null, LOGLEVEL_INFO); - } - } - catch (MAPIException $ex) { - throw new AuthenticationRequiredException($ex->getDisplayMessage()); - } - - if(!$this->session) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->Logon(): logon failed for user '%s'", $user)); - $this->defaultstore = false; - return false; - } - - // Get/open default store - $this->defaultstore = $this->openMessageStore($this->mainUser); - - if (mapi_last_hresult() == MAPI_E_FAILONEPROVIDER) - throw new HTTPReturnCodeException("Error connecting to ZCP (open store)", 503, null, LOGLEVEL_INFO); - - if($this->defaultstore === false) - throw new AuthenticationRequiredException(sprintf("ZarafaBackend->Logon(): User '%s' has no default store", $user)); - - $this->store = $this->defaultstore; - $this->storeName = $this->mainUser; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Logon(): User '%s' is authenticated",$user)); - - $this->isZPushEnabled(); - - // check if this is a Zarafa 7 store with unicode support - MAPIUtils::IsUnicodeStore($this->store); - return true; - } - - /** - * Setup the backend to work on a specific store or checks ACLs there. - * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be - * performed on this store (switch operations store). - * If the ACL check is enabled, this operation should just indicate the ACL status on - * the submitted store, without changing the store for operations. - * For the ACL status, the currently logged on user MUST have access rights on - * - the entire store - admin access if no folderid is sent, or - * - on a specific folderid in the store (secretary/full access rights) - * - * The ACLcheck MUST fail if a folder of the authenticated user is checked! - * - * @param string $store target store, could contain a "domain\user" value - * @param boolean $checkACLonly if set to true, Setup() should just check ACLs - * @param string $folderid if set, only ACLs on this folderid are relevant - * - * @access public - * @return boolean - */ - public function Setup($store, $checkACLonly = false, $folderid = false) { - list($user, $domain) = Utils::SplitDomainUser($store); - - if (!isset($this->mainUser)) - return false; - - if ($user === false) - $user = $this->mainUser; - - // This is a special case. A user will get it's entire folder structure by the foldersync by default. - // The ACL check is executed when an additional folder is going to be sent to the mobile. - // Configured that way the user could receive the same folderid twice, with two different names. - if ($this->mainUser == $user && $checkACLonly && $folderid) { - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->Setup(): Checking ACLs for folder of the users defaultstore. Fail is forced to avoid folder duplications on mobile."); - return false; - } - - // get the users store - $userstore = $this->openMessageStore($user); - - // only proceed if a store was found, else return false - if ($userstore) { - // only check permissions - if ($checkACLonly == true) { - // check for admin rights - if (!$folderid) { - if ($user != $this->mainUser) { - $zarafauserinfo = @mapi_zarafa_getuser_by_name($this->defaultstore, $this->mainUser); - $admin = (isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'])?true:false; - } - // the user has always full access to his own store - else - $admin = true; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Setup(): Checking for admin ACLs on store '%s': '%s'", $user, Utils::PrintAsString($admin))); - return $admin; - } - // check 'secretary' permissions on this folder - else { - $rights = $this->hasSecretaryACLs($userstore, $folderid); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Setup(): Checking for secretary ACLs on '%s' of store '%s': '%s'", $folderid, $user, Utils::PrintAsString($rights))); - return $rights; - } - } - - // switch operations store - // this should also be done if called with user = mainuser or user = false - // which means to switch back to the default store - else { - // switch active store - $this->store = $userstore; - $this->storeName = $user; - return true; - } - } - return false; - } - - /** - * Logs off - * Free/Busy information is updated for modified calendars - * This is done after the synchronization process is completed - * - * @access public - * @return boolean - */ - public function Logoff() { - // update if the calendar which received incoming changes - foreach($this->importedFolders as $folderid => $store) { - // open the root of the store - $storeprops = mapi_getprops($store, array(PR_USER_ENTRYID)); - $root = mapi_msgstore_openentry($store); - if (!$root) - continue; - - // get the entryid of the main calendar of the store and the calendar to be published - $rootprops = mapi_getprops($root, array(PR_IPM_APPOINTMENT_ENTRYID)); - $entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid)); - - // only publish free busy for the main calendar - if(isset($rootprops[PR_IPM_APPOINTMENT_ENTRYID]) && $rootprops[PR_IPM_APPOINTMENT_ENTRYID] == $entryid) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ZarafaBackend->Logoff(): Updating freebusy information on folder id '%s'", $folderid)); - $calendar = mapi_msgstore_openentry($store, $entryid); - - $pub = new FreeBusyPublish($this->session, $store, $calendar, $storeprops[PR_USER_ENTRYID]); - $pub->publishFB(time() - (7 * 24 * 60 * 60), 6 * 30 * 24 * 60 * 60); // publish from one week ago, 6 months ahead - } - } - - return true; - } - - /** - * Returns an array of SyncFolder types with the entire folder hierarchy - * on the server (the array itself is flat, but refers to parents via the 'parent' property - * - * provides AS 1.0 compatibility - * - * @access public - * @return array SYNC_FOLDER - */ - public function GetHierarchy() { - $folders = array(); - $importer = false; - $mapiprovider = new MAPIProvider($this->session, $this->store); - - $rootfolder = mapi_msgstore_openentry($this->store); - $rootfolderprops = mapi_getprops($rootfolder, array(PR_SOURCE_KEY)); - $rootfoldersourcekey = bin2hex($rootfolderprops[PR_SOURCE_KEY]); - - $hierarchy = mapi_folder_gethierarchytable($rootfolder, CONVENIENT_DEPTH); - $rows = mapi_table_queryallrows($hierarchy, array(PR_ENTRYID)); - - foreach ($rows as $row) { - $mapifolder = mapi_msgstore_openentry($this->store, $row[PR_ENTRYID]); - $folder = $mapiprovider->GetFolder($mapifolder); - - if (isset($folder->parentid) && $folder->parentid != $rootfoldersourcekey) - $folders[] = $folder; - } - - return $folders; - } - - /** - * Returns the importer to process changes from the mobile - * If no $folderid is given, hierarchy importer is expected - * - * @param string $folderid (opt) - * - * @access public - * @return object(ImportChanges) - */ - public function GetImporter($folderid = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->GetImporter() folderid: '%s'", Utils::PrintAsString($folderid))); - if($folderid !== false) { - // check if the user of the current store has permissions to import to this folderid - if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->GetImporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid))); - return false; - } - $this->importedFolders[$folderid] = $this->store; - return new ImportChangesICS($this->session, $this->store, hex2bin($folderid)); - } - else - return new ImportChangesICS($this->session, $this->store); - } - - /** - * Returns the exporter to send changes to the mobile - * If no $folderid is given, hierarchy exporter is expected - * - * @param string $folderid (opt) - * - * @access public - * @return object(ExportChanges) - * @throws StatusException - */ - public function GetExporter($folderid = false) { - if($folderid !== false) { - // check if the user of the current store has permissions to export from this folderid - if ($this->storeName != $this->mainUser && !$this->hasSecretaryACLs($this->store, $folderid)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->GetExporter(): missing permissions on folderid: '%s'.", Utils::PrintAsString($folderid))); - return false; - } - return new ExportChangesICS($this->session, $this->store, hex2bin($folderid)); - } - else - return new ExportChangesICS($this->session, $this->store); - } - - /** - * Sends an e-mail - * This messages needs to be saved into the 'sent items' folder - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm) { - // Check if imtomapi function is available and use it to send the mime message. - // It is available since ZCP 7.0.6 - // @see http://jira.zarafa.com/browse/ZCP-9508 - if (!(function_exists('mapi_feature') && mapi_feature('INETMAPI_IMTOMAPI'))) { - throw new StatusException("ZarafaBackend->SendMail(): ZCP version is too old, INETMAPI_IMTOMAPI is not available. Install at least ZCP version 7.0.6 or later.", SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED, null, LOGLEVEL_FATAL); - return false; - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): RFC822: %d bytes forward-id: '%s' reply-id: '%s' parent-id: '%s' SaveInSent: '%s' ReplaceMIME: '%s'", - strlen($sm->mime), Utils::PrintAsString($sm->forwardflag), Utils::PrintAsString($sm->replyflag), - Utils::PrintAsString((isset($sm->source->folderid) ? $sm->source->folderid : false)), - Utils::PrintAsString(($sm->saveinsent)), Utils::PrintAsString(isset($sm->replacemime)) )); - - // by splitting the message in several lines we can easily grep later - foreach(preg_split("/((\r)?\n)/", $sm->mime) as $rfc822line) - ZLog::Write(LOGLEVEL_WBXML, "RFC822: ". $rfc822line); - - $sendMailProps = MAPIMapping::GetSendMailProperties(); - $sendMailProps = getPropIdsFromStrings($this->store, $sendMailProps); - - // Open the outbox and create the message there - $storeprops = mapi_getprops($this->store, array($sendMailProps["outboxentryid"], $sendMailProps["ipmsentmailentryid"])); - if(isset($storeprops[$sendMailProps["outboxentryid"]])) - $outbox = mapi_msgstore_openentry($this->store, $storeprops[$sendMailProps["outboxentryid"]]); - - if(!$outbox) - throw new StatusException(sprintf("ZarafaBackend->SendMail(): No Outbox found or unable to create message: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_SERVERERROR); - - $mapimessage = mapi_folder_createmessage($outbox); - - //message properties to be set - $mapiprops = array(); - // only save the outgoing in sent items folder if the mobile requests it - $mapiprops[$sendMailProps["sentmailentryid"]] = $storeprops[$sendMailProps["ipmsentmailentryid"]]; - - ZLog::Write(LOGLEVEL_DEBUG, "Use the mapi_inetmapi_imtomapi function"); - $ab = mapi_openaddressbook($this->session); - mapi_inetmapi_imtomapi($this->session, $this->store, $ab, $mapimessage, $sm->mime, array()); - - // Set the appSeqNr so that tracking tab can be updated for meeting request updates - // @see http://jira.zarafa.com/browse/ZP-68 - $meetingRequestProps = MAPIMapping::GetMeetingRequestProperties(); - $meetingRequestProps = getPropIdsFromStrings($this->store, $meetingRequestProps); - $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS, $meetingRequestProps["goidtag"], $sendMailProps["internetcpid"], $sendMailProps["body"], $sendMailProps["html"], $sendMailProps["rtf"], $sendMailProps["rtfinsync"])); - - // Convert sent message's body to UTF-8 if it was a HTML message. - // @see http://jira.zarafa.com/browse/ZP-505 and http://jira.zarafa.com/browse/ZP-555 - if (isset($props[$sendMailProps["internetcpid"]]) && $props[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8 && MAPIUtils::GetNativeBodyType($props) == SYNC_BODYPREFERENCE_HTML) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sent email cpid is not unicode (%d). Set it to unicode and convert email html body.", $props[$sendMailProps["internetcpid"]])); - $mapiprops[$sendMailProps["internetcpid"]] = INTERNET_CPID_UTF8; - - $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); - $bodyHtml = Utils::ConvertCodepageStringToUtf8($props[$sendMailProps["internetcpid"]], $bodyHtml); - $mapiprops[$sendMailProps["html"]] = $bodyHtml; - - mapi_setprops($mapimessage, $mapiprops); - } - if (stripos($props[PR_MESSAGE_CLASS], "IPM.Schedule.Meeting.Resp.") === 0) { - // search for calendar items using goid - $mr = new Meetingrequest($this->store, $mapimessage); - $appointments = $mr->findCalendarItems($props[$meetingRequestProps["goidtag"]]); - if (is_array($appointments) && !empty($appointments)) { - $app = mapi_msgstore_openentry($this->store, $appointments[0]); - $appprops = mapi_getprops($app, array($meetingRequestProps["appSeqNr"])); - if (isset($appprops[$meetingRequestProps["appSeqNr"]]) && $appprops[$meetingRequestProps["appSeqNr"]]) { - $mapiprops[$meetingRequestProps["appSeqNr"]] = $appprops[$meetingRequestProps["appSeqNr"]]; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Set sequence number to:%d", $appprops[$meetingRequestProps["appSeqNr"]])); - } - } - } - - // Delete the PR_SENT_REPRESENTING_* properties because some android devices - // do not send neither From nor Sender header causing empty PR_SENT_REPRESENTING_NAME and - // PR_SENT_REPRESENTING_EMAIL_ADDRESS properties and "broken" PR_SENT_REPRESENTING_ENTRYID - // which results in spooler not being able to send the message. - // @see http://jira.zarafa.com/browse/ZP-85 - mapi_deleteprops($mapimessage, - array( $sendMailProps["sentrepresentingname"], $sendMailProps["sentrepresentingemail"], $sendMailProps["representingentryid"], - $sendMailProps["sentrepresentingaddt"], $sendMailProps["sentrepresentinsrchk"])); - - if(isset($sm->source->itemid) && $sm->source->itemid) { - // answering an email in a public/shared folder - if (!$this->Setup(ZPush::GetAdditionalSyncFolderStore($sm->source->folderid))) - throw new StatusException(sprintf("ZarafaBackend->SendMail() could not Setup() the backend for folder id '%s'", $sm->source->folderid), SYNC_COMMONSTATUS_SERVERERROR); - - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($sm->source->folderid), hex2bin($sm->source->itemid)); - if ($entryid) - $fwmessage = mapi_msgstore_openentry($this->store, $entryid); - - if(!isset($fwmessage) || !$fwmessage) - throw new StatusException(sprintf("ZarafaBackend->SendMail(): Could not open message id '%s' in folder id '%s' to be replied/forwarded: 0x%X", $sm->source->itemid, $sm->source->folderid, mapi_last_hresult()), SYNC_COMMONSTATUS_ITEMNOTFOUND); - - //update icon when forwarding or replying message - if ($sm->forwardflag) mapi_setprops($fwmessage, array(PR_ICON_INDEX=>262)); - elseif ($sm->replyflag) mapi_setprops($fwmessage, array(PR_ICON_INDEX=>261)); - mapi_savechanges($fwmessage); - - // only attach the original message if the mobile does not send it itself - if (!isset($sm->replacemime)) { - // get message's body in order to append forward or reply text - if (!isset($body)) { - $body = MAPIUtils::readPropStream($mapimessage, PR_BODY); - } - if (!isset($bodyHtml)) { - $bodyHtml = MAPIUtils::readPropStream($mapimessage, PR_HTML); - } - $cpid = mapi_getprops($fwmessage, array($sendMailProps["internetcpid"])); - if($sm->forwardflag) { - // attach the original attachments to the outgoing message - $this->copyAttachments($mapimessage, $fwmessage); - } - - // regarding the conversion @see ZP-470 - if (strlen($body) > 0) { - $fwbody = MAPIUtils::readPropStream($fwmessage, PR_BODY); - // if only the old message's cpid is set, convert from old charset to utf-8 - if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert plain forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); - $fwbody = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbody); - } - // otherwise to the general conversion - else { - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for plain forwarded message"); - $fwbody = w2u($fwbody); - } - - $mapiprops[$sendMailProps["body"]] = $body."\r\n\r\n".$fwbody; - } - - if (strlen($bodyHtml) > 0) { - $fwbodyHtml = MAPIUtils::readPropStream($fwmessage, PR_HTML); - // if only new message's cpid is set, convert to UTF-8 - if (isset($cpid[$sendMailProps["internetcpid"]]) && $cpid[$sendMailProps["internetcpid"]] != INTERNET_CPID_UTF8) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->SendMail(): convert html forwarded message charset (only fw set) from '%s' to '65001'", $cpid[$sendMailProps["internetcpid"]])); - $fwbodyHtml = Utils::ConvertCodepageStringToUtf8($cpid[$sendMailProps["internetcpid"]], $fwbodyHtml); - } - // otherwise to the general conversion - else { - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): no charset conversion done for html forwarded message"); - $fwbodyHtml = w2u($fwbodyHtml); - } - - $mapiprops[$sendMailProps["html"]] = $bodyHtml."

".$fwbodyHtml; - } - } - } - - mapi_setprops($mapimessage, $mapiprops); - mapi_message_savechanges($mapimessage); - mapi_message_submitmessage($mapimessage); - $hr = mapi_last_hresult(); - - if ($hr) - throw new StatusException(sprintf("ZarafaBackend->SendMail(): Error saving/submitting the message to the Outbox: 0x%X", mapi_last_hresult()), SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED); - - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->SendMail(): email submitted"); - return true; - } - - /** - * Returns all available data of a single message - * - * @param string $folderid - * @param string $id - * @param ContentParameters $contentparameters flag - * - * @access public - * @return object(SyncObject) - * @throws StatusException - */ - public function Fetch($folderid, $id, $contentparameters) { - // get the entry id of the message - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($id)); - if(!$entryid) - throw new StatusException(sprintf("BackendZarafa->Fetch('%s','%s'): Error getting entryid: 0x%X", $folderid, $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); - - // open the message - $message = mapi_msgstore_openentry($this->store, $entryid); - if(!$message) - throw new StatusException(sprintf("BackendZarafa->Fetch('%s','%s'): Error, unable to open message: 0x%X", $folderid, $id, mapi_last_hresult()), SYNC_STATUS_OBJECTNOTFOUND); - - // convert the mapi message into a SyncObject and return it - $mapiprovider = new MAPIProvider($this->session, $this->store); - - // override truncation - $contentparameters->SetTruncation(SYNC_TRUNCATION_ALL); - // TODO check for body preferences - return $mapiprovider->GetMessage($message, $contentparameters); - } - - /** - * Returns the waste basket - * - * @access public - * @return string - */ - public function GetWasteBasket() { - if ($this->wastebasket) { - return $this->wastebasket; - } - - $storeprops = mapi_getprops($this->defaultstore, array(PR_IPM_WASTEBASKET_ENTRYID)); - if (isset($storeprops[PR_IPM_WASTEBASKET_ENTRYID])) { - $wastebasket = mapi_msgstore_openentry($this->store, $storeprops[PR_IPM_WASTEBASKET_ENTRYID]); - $wastebasketprops = mapi_getprops($wastebasket, array(PR_SOURCE_KEY)); - if (isset($wastebasketprops[PR_SOURCE_KEY])) { - $this->wastebasket = bin2hex($wastebasketprops[PR_SOURCE_KEY]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Got waste basket with id '%s'", $this->wastebasket)); - return $this->wastebasket; - } - } - return false; - } - - /** - * Returns the content of the named attachment as stream - * - * @param string $attname - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->GetAttachmentData('%s')", $attname)); - - if(!strpos($attname, ":")) - throw new StatusException(sprintf("ZarafaBackend->GetAttachmentData('%s'): Error, attachment requested for non-existing item", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - list($id, $attachnum) = explode(":", $attname); - - $entryid = hex2bin($id); - $message = mapi_msgstore_openentry($this->store, $entryid); - if(!$message) - throw new StatusException(sprintf("ZarafaBackend->GetAttachmentData('%s'): Error, unable to open item for attachment data for id '%s' with: 0x%X", $attname, $id, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - $attach = mapi_message_openattach($message, $attachnum); - if(!$attach) - throw new StatusException(sprintf("ZarafaBackend->GetAttachmentData('%s'): Error, unable to open attachment number '%s' with: 0x%X", $attname, $attachnum, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - // get necessary attachment props - $attprops = mapi_getprops($attach, array(PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W, PR_ATTACH_METHOD)); - $attachment = new SyncItemOperationsAttachment(); - // check if it's an embedded message and open it in such a case - if (isset($attprops[PR_ATTACH_METHOD]) && $attprops[PR_ATTACH_METHOD] == ATTACH_EMBEDDED_MSG) { - $embMessage = mapi_attach_openobj($attach); - $addrbook = $this->getAddressbook(); - $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $embMessage, array('use_tnef' => -1)); - // set the default contenttype for this kind of messages - $attachment->contenttype = "message/rfc822"; - } - else - $stream = mapi_openpropertytostream($attach, PR_ATTACH_DATA_BIN); - - if(!$stream) - throw new StatusException(sprintf("ZarafaBackend->GetAttachmentData('%s'): Error, unable to open attachment data stream: 0x%X", $attname, mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - // put the mapi stream into a wrapper to get a standard stream - $attachment->data = MapiStreamWrapper::Open($stream); - if (isset($attprops[PR_ATTACH_MIME_TAG])) - $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG]; - elseif (isset($attprops[PR_ATTACH_MIME_TAG_W])) - $attachment->contenttype = $attprops[PR_ATTACH_MIME_TAG_W]; - //TODO default contenttype - return $attachment; - } - - - /** - * Deletes all contents of the specified folder. - * This is generally used to empty the trash (wastebasked), but could also be used on any - * other folder. - * - * @param string $folderid - * @param boolean $includeSubfolders (opt) also delete sub folders, default true - * - * @access public - * @return boolean - * @throws StatusException - */ - public function EmptyFolder($folderid, $includeSubfolders = true) { - $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid)); - if (!$folderentryid) - throw new StatusException(sprintf("BackendZarafa->EmptyFolder('%s','%s'): Error, unable to open folder (no entry id)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - $folder = mapi_msgstore_openentry($this->store, $folderentryid); - - if (!$folder) - throw new StatusException(sprintf("BackendZarafa->EmptyFolder('%s','%s'): Error, unable to open parent folder (open entry)", $folderid, Utils::PrintAsString($includeSubfolders)), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - - $flags = 0; - if ($includeSubfolders) - $flags = DEL_ASSOCIATED; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->EmptyFolder('%s','%s'): emptying folder",$folderid, Utils::PrintAsString($includeSubfolders))); - - // empty folder! - mapi_folder_emptyfolder($folder, $flags); - if (mapi_last_hresult()) - throw new StatusException(sprintf("BackendZarafa->EmptyFolder('%s','%s'): Error, mapi_folder_emptyfolder() failed: 0x%X", $folderid, Utils::PrintAsString($includeSubfolders), mapi_last_hresult()), SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - - return true; - } - - /** - * Processes a response to a meeting request. - * CalendarID is a reference and has to be set if a new calendar item is created - * - * @param string $requestid id of the object containing the request - * @param string $folderid id of the parent folder of $requestid - * @param string $response - * - * @access public - * @return string id of the created/updated calendar obj - * @throws StatusException - */ - public function MeetingResponse($requestid, $folderid, $response) { - // Use standard meeting response code to process meeting request - $reqentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid), hex2bin($requestid)); - if (!$reqentryid) - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s', '%s', '%s'): Error, unable to entryid of the message 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); - - $mapimessage = mapi_msgstore_openentry($this->store, $reqentryid); - if(!$mapimessage) - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, unable to open request message for response 0x%X", $requestid, $folderid, $response, mapi_last_hresult()), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); - - $meetingrequest = new Meetingrequest($this->store, $mapimessage, $this->session); - - if(!$meetingrequest->isMeetingRequest()) - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, attempt to respond to non-meeting request", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); - - if($meetingrequest->isLocalOrganiser()) - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error, attempt to response to meeting request that we organized", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); - - // Process the meeting response. We don't have to send the actual meeting response - // e-mail, because the device will send it itself. - switch($response) { - case 1: // accept - default: - $entryid = $meetingrequest->doAccept(false, false, false, false, false, false, true); // last true is the $userAction - break; - case 2: // tentative - $entryid = $meetingrequest->doAccept(true, false, false, false, false, false, true); // last true is the $userAction - break; - case 3: // decline - $meetingrequest->doDecline(false); - break; - } - - // F/B will be updated on logoff - - // We have to return the ID of the new calendar item, so do that here - $calendarid = ""; - if (isset($entryid)) { - $newitem = mapi_msgstore_openentry($this->store, $entryid); - // new item might be in a delegator's store. ActiveSync does not support accepting them. - if (!$newitem) { - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Object with entryid '%s' was not found in user's store (0x%X). It might be in a delegator's store.", $requestid, $folderid, $response, bin2hex($entryid), mapi_last_hresult()), SYNC_MEETRESPSTATUS_SERVERERROR, null, LOGLEVEL_WARN); - } - - $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); - $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); - } - - // on recurring items, the MeetingRequest class responds with a wrong entryid - if ($requestid == $calendarid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): returned calender id is the same as the requestid - re-searching", $requestid, $folderid, $response)); - - $props = MAPIMapping::GetMeetingRequestProperties(); - $props = getPropIdsFromStrings($this->store, $props); - - $messageprops = mapi_getprops($mapimessage, Array($props["goidtag"])); - $goid = $messageprops[$props["goidtag"]]; - - $items = $meetingrequest->findCalendarItems($goid); - - if (is_array($items)) { - $newitem = mapi_msgstore_openentry($this->store, $items[0]); - $newprops = mapi_getprops($newitem, array(PR_SOURCE_KEY)); - $calendarid = bin2hex($newprops[PR_SOURCE_KEY]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): found other calendar entryid", $requestid, $folderid, $response)); - } - - if ($requestid == $calendarid) - throw new StatusException(sprintf("BackendZarafa->MeetingResponse('%s','%s', '%s'): Error finding the accepted meeting response in the calendar", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_INVALIDMEETREQ); - } - - // delete meeting request from Inbox - $folderentryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid)); - $folder = mapi_msgstore_openentry($this->store, $folderentryid); - mapi_folder_deletemessages($folder, array($reqentryid), 0); - - return $calendarid; - } - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * Since Zarafa 7.0.5 such a sink is available. - * The Zarafa backend uses this method to initialize the sink with mapi. - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - if (!$this->notifications) { - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->HasChangesSink(): sink is not available"); - return false; - } - - $this->changesSink = @mapi_sink_create(); - - if (! $this->changesSink || mapi_last_hresult()) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->HasChangesSink(): sink could not be created with 0x%X", mapi_last_hresult())); - return false; - } - - ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->HasChangesSink(): created"); - - // advise the main store and also to check if the connection supports it - return $this->adviseStoreToSink($this->defaultstore); - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBackend->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if entryid can not be found for that folder - */ - public function ChangesSinkInitialize($folderid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->ChangesSinkInitialize(): folderid '%s'", $folderid)); - - $entryid = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($folderid)); - if (!$entryid) - return false; - - // add entryid to the monitored folders - $this->changesSinkFolders[$entryid] = $folderid; - - // advise the current store to the sink - return $this->adviseStoreToSink($this->store); - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - $notifications = array(); - $sinkresult = @mapi_sink_timedwait($this->changesSink, $timeout * 1000); - foreach ($sinkresult as $sinknotif) { - // check if something in the monitored folders changed - if (isset($sinknotif['parentid']) && array_key_exists($sinknotif['parentid'], $this->changesSinkFolders)) { - $notifications[] = $this->changesSinkFolders[$sinknotif['parentid']]; - } - // deletes and moves - if (isset($sinknotif['oldparentid']) && array_key_exists($sinknotif['oldparentid'], $this->changesSinkFolders)) { - $notifications[] = $this->changesSinkFolders[$sinknotif['oldparentid']]; - } - } - return $notifications; - } - - /** - * Applies settings to and gets informations from the device - * - * @param SyncObject $settings (SyncOOF or SyncUserInformation possible) - * - * @access public - * @return SyncObject $settings - */ - public function Settings($settings) { - if ($settings instanceof SyncOOF) { - $this->settingsOOF($settings); - } - - if ($settings instanceof SyncUserInformation) { - $this->settingsUserInformation($settings); - } - - return $settings; - } - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - if ($resolveRecipients instanceof SyncResolveRecipients) { - $resolveRecipients->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS; - $resolveRecipients->recipient = array(); - foreach ($resolveRecipients->to as $i => $to) { - $recipient = $this->resolveRecipient($to); - if ($recipient instanceof SyncResolveRecipient) { - $resolveRecipients->recipient[$i] = $recipient; - } - elseif (is_int($recipient)) { - $resolveRecipients->status = $recipient; - } - } - - return $resolveRecipients; - } - ZLog::Write(LOGLEVEL_WARN, "Not a valid SyncResolveRecipients object."); - // return a SyncResolveRecipients object so that sync doesn't fail - $r = new SyncResolveRecipients(); - $r->status = SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR; - $r->recipient = array(); - return $r; - } - - - /**---------------------------------------------------------------------------------------------------------- - * Implementation of the ISearchProvider interface - */ - - /** - * Indicates if a search type is supported by this SearchProvider - * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - return ($searchtype == ISearchProvider::SEARCH_GAL) || ($searchtype == ISearchProvider::SEARCH_MAILBOX); - } - - /** - * Searches the GAB of Zarafa - * Can be overwitten globally by configuring a SearchBackend - * - * @param string $searchquery - * @param string $searchrange - * - * @access public - * @return array - * @throws StatusException - */ - public function GetGALSearchResults($searchquery, $searchrange){ - // only return users whose displayName or the username starts with $name - //TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT - $addrbook = $this->getAddressbook(); - if ($addrbook) - $ab_entryid = mapi_ab_getdefaultdir($addrbook); - if ($ab_entryid) - $ab_dir = mapi_ab_openentry($addrbook, $ab_entryid); - if ($ab_dir) - $table = mapi_folder_getcontentstable($ab_dir); - - if (!$table) - throw new StatusException(sprintf("ZarafaBackend->GetGALSearchResults(): could not open addressbook: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED); - - $restriction = MAPIUtils::GetSearchRestriction(u2w($searchquery)); - mapi_table_restrict($table, $restriction); - mapi_table_sort($table, array(PR_DISPLAY_NAME => TABLE_SORT_ASCEND)); - - if (mapi_last_hresult()) - throw new StatusException(sprintf("ZarafaBackend->GetGALSearchResults(): could not apply restriction: 0x%X", mapi_last_hresult()), SYNC_SEARCHSTATUS_STORE_TOOCOMPLEX); - - //range for the search results, default symbian range end is 50, wm 99, - //so we'll use that of nokia - $rangestart = 0; - $rangeend = 50; - - if ($searchrange != '0') { - $pos = strpos($searchrange, '-'); - $rangestart = substr($searchrange, 0, $pos); - $rangeend = substr($searchrange, ($pos + 1)); - } - $items = array(); - - $querycnt = mapi_table_getrowcount($table); - //do not return more results as requested in range - $querylimit = (($rangeend + 1) < $querycnt) ? ($rangeend + 1) : $querycnt; - - if ($querycnt > 0) - $abentries = mapi_table_queryrows($table, array(PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_BUSINESS_TELEPHONE_NUMBER, PR_GIVEN_NAME, PR_SURNAME, PR_MOBILE_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_TITLE, PR_COMPANY_NAME, PR_OFFICE_LOCATION), $rangestart, $querylimit); - - for ($i = 0; $i < $querylimit; $i++) { - if (!isset($abentries[$i][PR_SMTP_ADDRESS])) { - ZLog::Write(LOGLEVEL_WARN, sprintf("The GAL entry '%s' does not have an email address and will be ignored.", w2u($abentries[$i][PR_DISPLAY_NAME]))); - continue; - } - - $items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_DISPLAY_NAME]); - - if (strlen(trim($items[$i][SYNC_GAL_DISPLAYNAME])) == 0) - $items[$i][SYNC_GAL_DISPLAYNAME] = w2u($abentries[$i][PR_ACCOUNT]); - - $items[$i][SYNC_GAL_ALIAS] = w2u($abentries[$i][PR_ACCOUNT]); - //it's not possible not get first and last name of an user - //from the gab and user functions, so we just set lastname - //to displayname and leave firstname unset - //this was changed in Zarafa 6.40, so we try to get first and - //last name and fall back to the old behaviour if these values are not set - if (isset($abentries[$i][PR_GIVEN_NAME])) - $items[$i][SYNC_GAL_FIRSTNAME] = w2u($abentries[$i][PR_GIVEN_NAME]); - if (isset($abentries[$i][PR_SURNAME])) - $items[$i][SYNC_GAL_LASTNAME] = w2u($abentries[$i][PR_SURNAME]); - - if (!isset($items[$i][SYNC_GAL_LASTNAME])) $items[$i][SYNC_GAL_LASTNAME] = $items[$i][SYNC_GAL_DISPLAYNAME]; - - $items[$i][SYNC_GAL_EMAILADDRESS] = w2u($abentries[$i][PR_SMTP_ADDRESS]); - //check if an user has an office number or it might produce warnings in the log - if (isset($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER])) - $items[$i][SYNC_GAL_PHONE] = w2u($abentries[$i][PR_BUSINESS_TELEPHONE_NUMBER]); - //check if an user has a mobile number or it might produce warnings in the log - if (isset($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER])) - $items[$i][SYNC_GAL_MOBILEPHONE] = w2u($abentries[$i][PR_MOBILE_TELEPHONE_NUMBER]); - //check if an user has a home number or it might produce warnings in the log - if (isset($abentries[$i][PR_HOME_TELEPHONE_NUMBER])) - $items[$i][SYNC_GAL_HOMEPHONE] = w2u($abentries[$i][PR_HOME_TELEPHONE_NUMBER]); - - if (isset($abentries[$i][PR_COMPANY_NAME])) - $items[$i][SYNC_GAL_COMPANY] = w2u($abentries[$i][PR_COMPANY_NAME]); - - if (isset($abentries[$i][PR_TITLE])) - $items[$i][SYNC_GAL_TITLE] = w2u($abentries[$i][PR_TITLE]); - - if (isset($abentries[$i][PR_OFFICE_LOCATION])) - $items[$i][SYNC_GAL_OFFICE] = w2u($abentries[$i][PR_OFFICE_LOCATION]); - } - $nrResults = count($items); - $items['range'] = ($nrResults > 0) ? $rangestart.'-'.($nrResults - 1) : '0-0'; - $items['searchtotal'] = $nrResults; - return $items; - } - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo) { - $searchFolder = $this->getSearchFolder(); - $searchRestriction = $this->getSearchRestriction($cpo); - $searchRange = explode('-', $cpo->GetSearchRange()); - $searchFolderId = $cpo->GetSearchFolderid(); - $searchFolders = array(); - // search only in required folders - if (!empty($searchFolderId)) { - $searchFolderEntryId = mapi_msgstore_entryidfromsourcekey($this->store, hex2bin($searchFolderId)); - $searchFolders[] = $searchFolderEntryId; - } - // if no folder was required then search in the entire store - else { - $tmp = mapi_getprops($this->store, array(PR_ENTRYID,PR_DISPLAY_NAME,PR_IPM_SUBTREE_ENTRYID)); - $searchFolders[] = $tmp[PR_IPM_SUBTREE_ENTRYID]; - } - $items = array(); - $flags = 0; - // if subfolders are required, do a recursive search - if ($cpo->GetSearchDeepTraversal()) { - $flags |= SEARCH_RECURSIVE; - } - - mapi_folder_setsearchcriteria($searchFolder, $searchRestriction, $searchFolders, $flags); - - $table = mapi_folder_getcontentstable($searchFolder); - $searchStart = time(); - // do the search and wait for all the results available - while (time() - $searchStart < SEARCH_WAIT) { - $searchcriteria = mapi_folder_getsearchcriteria($searchFolder); - if(($searchcriteria["searchstate"] & SEARCH_REBUILD) == 0) - break; // Search is done - sleep(1); - } - - // if the search range is set limit the result to it, otherwise return all found messages - $rows = (is_array($searchRange) && isset($searchRange[0]) && isset($searchRange[1])) ? - mapi_table_queryrows($table, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY), $searchRange[0], $searchRange[1] - $searchRange[0] + 1) : - mapi_table_queryrows($table, array(PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY), 0, SEARCH_MAXRESULTS); - - $cnt = count($rows); - $items['searchtotal'] = $cnt; - $items["range"] = $cpo->GetSearchRange(); - for ($i = 0; $i < $cnt; $i++) { - $items[$i]['class'] = 'Email'; - $items[$i]['longid'] = bin2hex($rows[$i][PR_PARENT_SOURCE_KEY]) . ":" . bin2hex($rows[$i][PR_SOURCE_KEY]); - $items[$i]['folderid'] = bin2hex($rows[$i][PR_PARENT_SOURCE_KEY]); - } - return $items; - } - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->TerminateSearch(): terminating search for pid %d", $pid)); - $storeProps = mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID)); - if (($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) { - ZLog::Write(LOGLEVEL_WARN, "Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder"); - return false; - } - - $finderfolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]); - if(mapi_last_hresult() != NOERROR) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to open search folder (0x%X)", mapi_last_hresult())); - return false; - } - - $hierarchytable = mapi_folder_gethierarchytable($finderfolder); - mapi_table_restrict($hierarchytable, - array(RES_CONTENT, - array( - FUZZYLEVEL => FL_PREFIX, - ULPROPTAG => PR_DISPLAY_NAME, - VALUE => array(PR_DISPLAY_NAME=>"Z-Push Search Folder ".$pid) - ) - ), - TBL_BATCH); - - $folders = mapi_table_queryallrows($hierarchytable, array(PR_ENTRYID, PR_DISPLAY_NAME, PR_LAST_MODIFICATION_TIME)); - foreach($folders as $folder) { - mapi_folder_deletefolder($finderfolder, $folder[PR_ENTRYID]); - } - return true; - } - - /** - * Disconnects from the current search provider - * - * @access public - * @return boolean - */ - public function Disconnect() { - return true; - } - - /** - * Returns the MAPI store ressource for a folderid - * This is not part of IBackend but necessary for the ImportChangesICS->MoveMessage() operation if - * the destination folder is not in the default store - * Note: The current backend store might be changed as IBackend->Setup() is executed - * - * @param string $store target store, could contain a "domain\user" value - if emtpy default store is returned - * @param string $folderid - * - * @access public - * @return Ressource/boolean - */ - public function GetMAPIStoreForFolderId($store, $folderid) { - if ($store == false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->GetMAPIStoreForFolderId('%s', '%s'): no store specified, returning default store", $store, $folderid)); - return $this->defaultstore; - } - - // setup the correct store - if ($this->Setup($store, false, $folderid)) { - return $this->store; - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->GetMAPIStoreForFolderId('%s', '%s'): store is not available", $store, $folderid)); - return false; - } - } - - /** - * Returns the email address and the display name of the user. Used by autodiscover. - * - * @param string $username The username - * - * @access public - * @return Array - */ - public function GetUserDetails($username) { - ZLog::Write(LOGLEVEL_WBXML, sprintf("ZarafaBackend->GetUserDetails for '%s'.", $username)); - $zarafauserinfo = @mapi_zarafa_getuser_by_name($this->store, $username); - $userDetails['emailaddress'] = (isset($zarafauserinfo['emailaddress']) && $zarafauserinfo['emailaddress']) ? $zarafauserinfo['emailaddress'] : false; - $userDetails['fullname'] = (isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname']) ? $zarafauserinfo['fullname'] : false; - return $userDetails; - } - - /** - * Returns the username of the currently active user - * - * @access public - * @return String - */ - public function GetCurrentUsername() { - return $this->storeName; - } - - /**---------------------------------------------------------------------------------------------------------- - * Private methods - */ - - /** - * Advises a store to the changes sink - * - * @param mapistore $store store to be advised - * - * @access private - * @return boolean - */ - private function adviseStoreToSink($store) { - // check if we already advised the store - if (!in_array($store, $this->changesSinkStores)) { - mapi_msgstore_advise($this->store, null, fnevObjectModified | fnevObjectCreated | fnevObjectMoved | fnevObjectDeleted, $this->changesSink); - $this->changesSinkStores[] = $store; - - if (mapi_last_hresult()) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->adviseStoreToSink(): failed to advised store '%s' with code 0x%X. Polling will be performed.", $this->store, mapi_last_hresult())); - return false; - } - else - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->adviseStoreToSink(): advised store '%s'", $this->store)); - } - return true; - } - - /** - * Open the store marked with PR_DEFAULT_STORE = TRUE - * if $return_public is set, the public store is opened - * - * @param string $user User which store should be opened - * - * @access public - * @return boolean - */ - private function openMessageStore($user) { - // During PING requests the operations store has to be switched constantly - // the cache prevents the same store opened several times - if (isset($this->storeCache[$user])) - return $this->storeCache[$user]; - - $entryid = false; - $return_public = false; - - if (strtoupper($user) == 'SYSTEM') - $return_public = true; - - // loop through the storestable if authenticated user of public folder - if ($user == $this->mainUser || $return_public === true) { - // Find the default store - $storestables = mapi_getmsgstorestable($this->session); - $result = mapi_last_hresult(); - - if ($result == NOERROR){ - $rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER)); - - foreach($rows as $row) { - if(!$return_public && isset($row[PR_DEFAULT_STORE]) && $row[PR_DEFAULT_STORE] == true) { - $entryid = $row[PR_ENTRYID]; - break; - } - if ($return_public && isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { - $entryid = $row[PR_ENTRYID]; - break; - } - } - } - } - else - $entryid = @mapi_msgstore_createentryid($this->defaultstore, $user); - - if($entryid) { - $store = @mapi_openmsgstore($this->session, $entryid); - - if (!$store) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->openMessageStore('%s'): Could not open store", $user)); - return false; - } - - // add this store to the cache - if (!isset($this->storeCache[$user])) - $this->storeCache[$user] = $store; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->openMessageStore('%s'): Found '%s' store: '%s'", $user, (($return_public)?'PUBLIC':'DEFAULT'),$store)); - return $store; - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZarafaBackend->openMessageStore('%s'): No store found for this user", $user)); - return false; - } - } - - private function hasSecretaryACLs($store, $folderid) { - $entryid = mapi_msgstore_entryidfromsourcekey($store, hex2bin($folderid)); - if (!$entryid) return false; - - $folder = mapi_msgstore_openentry($store, $entryid); - if (!$folder) return false; - - $props = mapi_getprops($folder, array(PR_RIGHTS)); - if (isset($props[PR_RIGHTS]) && - ($props[PR_RIGHTS] & ecRightsReadAny) && - ($props[PR_RIGHTS] & ecRightsCreate) && - ($props[PR_RIGHTS] & ecRightsEditOwned) && - ($props[PR_RIGHTS] & ecRightsDeleteOwned) && - ($props[PR_RIGHTS] & ecRightsEditAny) && - ($props[PR_RIGHTS] & ecRightsDeleteAny) && - ($props[PR_RIGHTS] & ecRightsFolderVisible) ) { - return true; - } - return false; - } - - /** - * The meta function for out of office settings. - * - * @param SyncObject $oof - * - * @access private - * @return void - */ - private function settingsOOF(&$oof) { - //if oof state is set it must be set of oof and get otherwise - if (isset($oof->oofstate)) { - $this->settingsOOFSEt($oof); - } - else { - $this->settingsOOFGEt($oof); - } - } - - /** - * Gets the out of office settings - * - * @param SyncObject $oof - * - * @access private - * @return void - */ - private function settingsOOFGEt(&$oof) { - $oofprops = mapi_getprops($this->defaultstore, array(PR_EC_OUTOFOFFICE, PR_EC_OUTOFOFFICE_MSG, PR_EC_OUTOFOFFICE_SUBJECT)); - $oof->oofstate = SYNC_SETTINGSOOF_DISABLED; - $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS; - if ($oofprops != false) { - $oof->oofstate = isset($oofprops[PR_EC_OUTOFOFFICE]) ? ($oofprops[PR_EC_OUTOFOFFICE] ? SYNC_SETTINGSOOF_GLOBAL : SYNC_SETTINGSOOF_DISABLED) : SYNC_SETTINGSOOF_DISABLED; - //TODO external and external unknown - $oofmessage = new SyncOOFMessage(); - $oofmessage->appliesToInternal = ""; - $oofmessage->enabled = $oof->oofstate; - $oofmessage->replymessage = (isset($oofprops[PR_EC_OUTOFOFFICE_MSG])) ? w2u($oofprops[PR_EC_OUTOFOFFICE_MSG]) : ""; - $oofmessage->bodytype = $oof->bodytype; - unset($oofmessage->appliesToExternal, $oofmessage->appliesToExternalUnknown); - $oof->oofmessage[] = $oofmessage; - } - else { - ZLog::Write(LOGLEVEL_WARN, "Unable to get out of office information"); - } - - //unset body type for oof in order not to stream it - unset($oof->bodytype); - } - - /** - * Sets the out of office settings. - * - * @param SyncObject $oof - * - * @access private - * @return void - */ - private function settingsOOFSEt(&$oof) { - $oof->Status = SYNC_SETTINGSSTATUS_SUCCESS; - $props = array(); - if ($oof->oofstate == SYNC_SETTINGSOOF_GLOBAL || $oof->oofstate == SYNC_SETTINGSOOF_TIMEBASED) { - $props[PR_EC_OUTOFOFFICE] = true; - foreach ($oof->oofmessage as $oofmessage) { - if (isset($oofmessage->appliesToInternal)) { - $props[PR_EC_OUTOFOFFICE_MSG] = isset($oofmessage->replymessage) ? u2w($oofmessage->replymessage) : ""; - $props[PR_EC_OUTOFOFFICE_SUBJECT] = "Out of office"; - } - } - } - elseif($oof->oofstate == SYNC_SETTINGSOOF_DISABLED) { - $props[PR_EC_OUTOFOFFICE] = false; - } - - if (!empty($props)) { - @mapi_setprops($this->defaultstore, $props); - $result = mapi_last_hresult(); - if ($result != NOERROR) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Setting oof information failed (%X)", $result)); - return false; - } - } - - return true; - } - - /** - * Gets the user's email address from server - * - * @param SyncObject $userinformation - * - * @access private - * @return void - */ - private function settingsUserInformation(&$userinformation) { - if (!isset($this->defaultstore) || !isset($this->mainUser)) { - ZLog::Write(LOGLEVEL_ERROR, "The store or user are not available for getting user information"); - return false; - } - $user = mapi_zarafa_getuser($this->defaultstore, $this->mainUser); - if ($user != false) { - $userinformation->Status = SYNC_SETTINGSSTATUS_USERINFO_SUCCESS; - $userinformation->emailaddresses[] = $user["emailaddress"]; - return true; - } - ZLog::Write(LOGLEVEL_ERROR, sprintf("Getting user information failed: mapi_zarafa_getuser(%X)", mapi_last_hresult())); - return false; - } - - /** - * Sets the importance and priority of a message from a RFC822 message headers. - * - * @param int $xPriority - * @param array $mapiprops - * - * @return void - */ - private function getImportanceAndPriority($xPriority, &$mapiprops, $sendMailProps) { - switch($xPriority) { - case 1: - case 2: - $priority = PRIO_URGENT; - $importance = IMPORTANCE_HIGH; - break; - case 4: - case 5: - $priority = PRIO_NONURGENT; - $importance = IMPORTANCE_LOW; - break; - case 3: - default: - $priority = PRIO_NORMAL; - $importance = IMPORTANCE_NORMAL; - break; - } - $mapiprops[$sendMailProps["importance"]] = $importance; - $mapiprops[$sendMailProps["priority"]] = $priority; - } - - /** - * Copies attachments from one message to another. - * - * @param MAPIMessage $toMessage - * @param MAPIMessage $fromMessage - * - * @return void - */ - private function copyAttachments(&$toMessage, $fromMessage) { - $attachtable = mapi_message_getattachmenttable($fromMessage); - $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM)); - - foreach($rows as $row) { - if(isset($row[PR_ATTACH_NUM])) { - $attach = mapi_message_openattach($fromMessage, $row[PR_ATTACH_NUM]); - $newattach = mapi_message_createattach($toMessage); - mapi_copyto($attach, array(), array(), $newattach, 0); - mapi_savechanges($newattach); - } - } - } - - /** - * Function will create a search folder in FINDER_ROOT folder - * if folder exists then it will open it - * - * @see createSearchFolder($store, $openIfExists = true) function in the webaccess - * - * @return mapiFolderObject $folder created search folder - */ - private function getSearchFolder() { - // create new or open existing search folder - $searchFolderRoot = $this->getSearchFoldersRoot($this->store); - if($searchFolderRoot === false) { - // error in finding search root folder - // or store doesn't support search folders - return false; - } - - $searchFolder = $this->createSearchFolder($searchFolderRoot); - - if($searchFolder !== false && mapi_last_hresult() == NOERROR) { - return $searchFolder; - } - return false; - } - - /** - * Function will open FINDER_ROOT folder in root container - * public folder's don't have FINDER_ROOT folder - * - * @see getSearchFoldersRoot($store) function in the webaccess - * - * @return mapiFolderObject root folder for search folders - */ - private function getSearchFoldersRoot() { - // check if we can create search folders - $storeProps = mapi_getprops($this->store, array(PR_STORE_SUPPORT_MASK, PR_FINDER_ENTRYID)); - if(($storeProps[PR_STORE_SUPPORT_MASK] & STORE_SEARCH_OK) != STORE_SEARCH_OK) { - ZLog::Write(LOGLEVEL_WARN, "Store doesn't support search folders. Public store doesn't have FINDER_ROOT folder"); - return false; - } - - // open search folders root - $searchRootFolder = mapi_msgstore_openentry($this->store, $storeProps[PR_FINDER_ENTRYID]); - if(mapi_last_hresult() != NOERROR) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to open search folder (0x%X)", mapi_last_hresult())); - return false; - } - - return $searchRootFolder; - } - - - /** - * Creates a search folder if it not exists or opens an existing one - * and returns it. - * - * @param mapiFolderObject $searchFolderRoot - * - * @return mapiFolderObject - */ - private function createSearchFolder($searchFolderRoot) { - $folderName = "Z-Push Search Folder ".@getmypid(); - $searchFolders = mapi_folder_gethierarchytable($searchFolderRoot); - $restriction = array( - RES_CONTENT, - array( - FUZZYLEVEL => FL_PREFIX, - ULPROPTAG => PR_DISPLAY_NAME, - VALUE => array(PR_DISPLAY_NAME=>$folderName) - ) - ); - //restrict the hierarchy to the z-push search folder only - mapi_table_restrict($searchFolders, $restriction); - if (mapi_table_getrowcount($searchFolders)) { - $searchFolder = mapi_table_queryrows($searchFolders, array(PR_ENTRYID), 0, 1); - - return mapi_msgstore_openentry($this->store, $searchFolder[0][PR_ENTRYID]); - } - return mapi_folder_createfolder($searchFolderRoot, $folderName, null, 0, FOLDER_SEARCH); - } - - /** - * Creates a search restriction - * - * @param ContentParameter $cpo - * @return array - */ - private function getSearchRestriction($cpo) { - $searchText = $cpo->GetSearchFreeText(); - - $searchGreater = strtotime($cpo->GetSearchValueGreater()); - $searchLess = strtotime($cpo->GetSearchValueLess()); - - if (version_compare(phpversion(),'5.3.4') < 0) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Your system's PHP version (%s) might not correctly process unicode strings. Search containing such characters might not return correct results. It is recommended to update to at least PHP 5.3.4. See ZP-541 for more information.", phpversion())); - } - // split the search on whitespache and look for every word - $searchText = preg_split("/\W+/u", $searchText); - $searchProps = array(PR_BODY, PR_SUBJECT, PR_DISPLAY_TO, PR_DISPLAY_CC, PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_SENT_REPRESENTING_NAME, PR_SENT_REPRESENTING_EMAIL_ADDRESS); - $resAnd = array(); - foreach($searchText as $term) { - $resOr = array(); - - foreach($searchProps as $property) { - array_push($resOr, - array(RES_CONTENT, - array( - FUZZYLEVEL => FL_SUBSTRING|FL_IGNORECASE, - ULPROPTAG => $property, - VALUE => u2w($term) - ) - ) - ); - } - array_push($resAnd, array(RES_OR, $resOr)); - } - - // add time range restrictions - if ($searchGreater) { - array_push($resAnd, array(RES_PROPERTY, array(RELOP => RELOP_GE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => array(PR_MESSAGE_DELIVERY_TIME => $searchGreater)))); // RES_AND; - } - if ($searchLess) { - array_push($resAnd, array(RES_PROPERTY, array(RELOP => RELOP_LE, ULPROPTAG => PR_MESSAGE_DELIVERY_TIME, VALUE => array(PR_MESSAGE_DELIVERY_TIME => $searchLess)))); - } - $mapiquery = array(RES_AND, $resAnd); - - return $mapiquery; - } - - /** - * Resolve recipient based on his email address. - * - * @param string $to - * - * @return SyncResolveRecipient|boolean - */ - private function resolveRecipient($to) { - $recipient = $this->resolveRecipientGAL($to); - - if ($recipient !== false) { - return $recipient; - } - - $recipient = $this->resolveRecipientContact($to); - - if ($recipient !== false) { - return $recipient; - } - - return false; - } - - /** - * Resolves recipient from the GAL and gets his certificates. - * - * @param string $to - * @return SyncResolveRecipient|boolean - */ - private function resolveRecipientGAL($to) { - $addrbook = $this->getAddressbook(); - $ab_entryid = mapi_ab_getdefaultdir($addrbook); - if ($ab_entryid) - $ab_dir = mapi_ab_openentry($addrbook, $ab_entryid); - if ($ab_dir) - $table = mapi_folder_getcontentstable($ab_dir); - - // if (!$table) - // throw new StatusException(sprintf("ZarafaBackend->resolveRecipient(): could not open addressbook: 0x%X", mapi_last_hresult()), SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP); - - if (!$table) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to open addressbook:0x%X", mapi_last_hresult())); - return false; - } - - $restriction = MAPIUtils::GetSearchRestriction(u2w($to)); - mapi_table_restrict($table, $restriction); - - $querycnt = mapi_table_getrowcount($table); - if ($querycnt > 0) { - $abentries = mapi_table_queryrows($table, array(PR_DISPLAY_NAME, PR_EMS_AB_TAGGED_X509_CERT), 0, 1); - $certificates = - // check if there are any certificates available - (isset($abentries[0][PR_EMS_AB_TAGGED_X509_CERT]) && is_array($abentries[0][PR_EMS_AB_TAGGED_X509_CERT]) && count($abentries[0][PR_EMS_AB_TAGGED_X509_CERT])) ? - $this->getCertificates($abentries[0][PR_EMS_AB_TAGGED_X509_CERT], $querycnt) : false; - if ($certificates === false) { - // the recipient does not have a valid certificate, set the appropriate status - ZLog::Write(LOGLEVEL_INFO, sprintf("No certificates found for '%s'", $to)); - $certificates = $this->getCertificates(false); - } - $recipient = $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_GAL, w2u($abentries[0][PR_DISPLAY_NAME]), $to, $certificates); - return $recipient; - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("No recipient found for: '%s'", $to)); - return SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP; - } - return false; - } - - /** - * Resolves recipient from the contact list and gets his certificates. - * - * @param string $to - * - * @return SyncResolveRecipient|boolean - */ - private function resolveRecipientContact($to) { - // go through all contact folders of the user and - // check if there's a contact with the given email address - $root = mapi_msgstore_openentry($this->defaultstore); - if (!$root) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("Unable to open default store: 0x%X", mapi_last_hresult)); - } - $rootprops = mapi_getprops($root, array(PR_IPM_CONTACT_ENTRYID)); - $contacts = $this->getContactsFromFolder($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID], $to); - $recipients = array(); - - if ($contacts !== false) { - // create resolve recipient object - foreach ($contacts as $contact) { - $certificates = - // check if there are any certificates available - (isset($contact[PR_USER_X509_CERTIFICATE]) && is_array($contact[PR_USER_X509_CERTIFICATE]) && count($contact[PR_USER_X509_CERTIFICATE])) ? - $this->getCertificates($contact[PR_USER_X509_CERTIFICATE], 1) : false; - - if ($certificates !== false) { - return $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, u2w($contact[PR_DISPLAY_NAME]), $to, $certificates); - } - } - } - - $contactfolder = mapi_msgstore_openentry($this->defaultstore, $rootprops[PR_IPM_CONTACT_ENTRYID]); - $subfolders = MAPIUtils::GetSubfoldersForType($contactfolder, "IPF.Contact"); - foreach($subfolders as $folder) { - $contacts = $this->getContactsFromFolder($this->defaultstore, $folder[PR_ENTRYID], $to); - if ($contacts !== false) { - foreach ($contacts as $contact) { - $certificates = - // check if there are any certificates available - (isset($contact[PR_USER_X509_CERTIFICATE]) && is_array($contact[PR_USER_X509_CERTIFICATE]) && count($contact[PR_USER_X509_CERTIFICATE])) ? - $this->getCertificates($contact[PR_USER_X509_CERTIFICATE], 1) : false; - - if ($certificates !== false) { - return $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, u2w($contact[PR_DISPLAY_NAME]), $to, $certificates); - } - } - } - } - - // search contacts in public folders - $storestables = mapi_getmsgstorestable($this->session); - $result = mapi_last_hresult(); - - if ($result == NOERROR){ - $rows = mapi_table_queryallrows($storestables, array(PR_ENTRYID, PR_DEFAULT_STORE, PR_MDB_PROVIDER)); - foreach($rows as $row) { - if (isset($row[PR_MDB_PROVIDER]) && $row[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { - // TODO refactor public store - $publicstore = mapi_openmsgstore($this->session, $row[PR_ENTRYID]); - $publicfolder = mapi_msgstore_openentry($publicstore); - - $subfolders = MAPIUtils::GetSubfoldersForType($publicfolder, "IPF.Contact"); - if ($subfolders !== false) { - foreach($subfolders as $folder) { - $contacts = $this->getContactsFromFolder($publicstore, $folder[PR_ENTRYID], $to); - if ($contacts !== false) { - foreach ($contacts as $contact) { - $certificates = - // check if there are any certificates available - (isset($contact[PR_USER_X509_CERTIFICATE]) && is_array($contact[PR_USER_X509_CERTIFICATE]) && count($contact[PR_USER_X509_CERTIFICATE])) ? - $this->getCertificates($contact[PR_USER_X509_CERTIFICATE], 1) : false; - - if ($certificates !== false) { - return $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, u2w($contact[PR_DISPLAY_NAME]), $to, $certificates); - } - } - } - } - } - break; - } - } - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to open public store: 0x%X", $result)); - } - - $certificates = $this->getCertificates(false); - return $this->createResolveRecipient(SYNC_RESOLVERECIPIENTS_TYPE_CONTACT, $to, $to, $certificates); - } - - /** - * Creates SyncRRCertificates object for ResolveRecipients - * - * @param binary $certificates - * @param int $recipientCount - * - * @return SyncRRCertificates - */ - private function getCertificates($certificates, $recipientCount = 0) { - $cert = new SyncRRCertificates(); - if ($certificates === false) { - $cert->status = SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_NOVALIDCERT; - return $cert; - } - $cert->status = SYNC_RESOLVERECIPSSTATUS_SUCCESS; - $cert->certificatecount = count ($certificates); - $cert->recipientcount = $recipientCount; - $cert->certificate = array(); - foreach ($certificates as $certificate) { - $cert->certificate[] = base64_encode($certificate); - } - return $cert; - } - - /** - * - * @param int $type - * @param string $displayname - * @param string $email - * @param array $certificates - * - * @return SyncResolveRecipient - */ - private function createResolveRecipient($type, $displayname, $email, $certificates) { - $recipient = new SyncResolveRecipient(); - $recipient->type = $type; - $recipient->displayname = $displayname; - $recipient->emailaddress = $email; - $recipient->certificates = $certificates; - if ($recipient->certificates === false) { - // the recipient does not have a valid certificate, set the appropriate status - ZLog::Write(LOGLEVEL_INFO, sprintf("No certificates found for '%s'", $email)); - $cert = new SyncRRCertificates(); - $cert->status = SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_NOVALIDCERT; - $recipient->certificates = $cert; - } - return $recipient; - } - - /** - * Returns contacts matching given email address from a folder. - * - * @param MAPIStore $store - * @param binary $folderEntryid - * @param string $email - * - * @return array|boolean - */ - private function getContactsFromFolder($store, $folderEntryid, $email) { - $folder = mapi_msgstore_openentry($store, $folderEntryid); - $folderContent = mapi_folder_getcontentstable($folder); - mapi_table_restrict($folderContent, MAPIUtils::GetEmailAddressRestriction($store, $email)); - // TODO max limit - if (mapi_table_getrowcount($folderContent) > 0) { - return mapi_table_queryallrows($folderContent, array(PR_DISPLAY_NAME, PR_USER_X509_CERTIFICATE)); - } - return false; - } - - /** - * Get MAPI addressbook object - * - * @access private - * @return MAPIAddressbook object to be used with mapi_ab_* or false on failure - */ - private function getAddressbook() { - if (isset($this->addressbook) && $this->addressbook) { - return $this->addressbook; - } - $this->addressbook = mapi_openaddressbook($this->session); - $result = mapi_last_hresult(); - if ($result && $this->addressbook === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("MAPIProvider->getAddressbook error opening addressbook 0x%X", $result)); - return false; - } - return $this->addressbook; - } - - /** - * Checks if the user is not disabled for Z-Push. - * - * @access private - * @throws FatalException if user is disabled for Z-Push - * - * @return boolean - */ - private function isZPushEnabled() { - $addressbook = $this->getAddressbook(); - $userEntryid = mapi_getprops($this->store, array(PR_MAILBOX_OWNER_ENTRYID)); - $mailuser = mapi_ab_openentry($addressbook, $userEntryid[PR_MAILBOX_OWNER_ENTRYID]); - $enabledFeatures = mapi_getprops($mailuser, array(PR_EC_DISABLED_FEATURES)); - if (isset($enabledFeatures[PR_EC_DISABLED_FEATURES]) && is_array($enabledFeatures[PR_EC_DISABLED_FEATURES]) && in_array(self::ZPUSH_ENABLED, $enabledFeatures[PR_EC_DISABLED_FEATURES])) { - throw new FatalException("User is disabled for Z-Push."); - } - return true; - } -} - -/** - * DEPRECATED legacy class - */ -class BackendICS extends BackendZarafa {} diff --git a/sources/composer.json b/sources/composer.json deleted file mode 100644 index e7f29fa..0000000 --- a/sources/composer.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "autoload": { - "classmap": ["autodiscover/", "include/", "lib/"], - "files": ["version.php", "lib/core/zpush-utils.php", "lib/core/zpushdefs.php", "lib/utils/compat.php"] - } -} diff --git a/sources/config.php b/sources/config.php deleted file mode 100644 index 9b51d70..0000000 --- a/sources/config.php +++ /dev/null @@ -1,369 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -/********************************************************************************** - * Default settings - */ - // Defines the default time zone, change e.g. to "Europe/London" if necessary - define('TIMEZONE', ''); - - // Defines the base path on the server - define('BASE_PATH', dirname(__FILE__) . '/'); - - // Try to set unlimited timeout - define('SCRIPT_TIMEOUT', 0); - - // Your PHP could have a bug when base64 encoding: https://bugs.php.net/bug.php?id=68532 - // NOTE: Run "php testing/testing-bug68532fixed.php" to know what value put here - define('BUG68532FIXED', true); - - // When accessing through a proxy, the "X-Forwarded-For" header contains the original remote IP - define('USE_X_FORWARDED_FOR_HEADER', false); - - // When using client certificates, we can check if the login sent matches the owner of the certificate. - // This setting specifies the owner parameter in the certificate to look at. - define("CERTIFICATE_OWNER_PARAMETER", "SSL_CLIENT_S_DN_CN"); - - // Location of the trusted CA, e.g. '/etc/ssl/certs/EmailCA.pem' - // Uncomment and modify the following line if the validation of the certificates fails. - // define('CAINFO', '/etc/ssl/certs/EmailCA.pem'); - - /* - * Whether to use the complete email address as a login name - * (e.g. user@company.com) or the username only (user). - * This is required for Z-Push to work properly after autodiscover. - * Possible values: - * false - use the username only (default). - * true - use the complete email address. - */ - define('USE_FULLEMAIL_FOR_LOGIN', true); - -/********************************************************************************** - * Device pre-authorization. Useful when using Z-Push as a standalone product. - * - * It will use the STATE_MACHINE specified below, to store the users/devices - * FILE => STATE_DIR/PreAuthUserDevices - * SQL => auth_users - * - * FALSE => default - * TRUE - */ - define('PRE_AUTHORIZE_USERS', false); - - // New users are pre-authorized automatically - define('PRE_AUTHORIZE_NEW_USERS', false); - - // New devices are pre-authorized automatically for pre-authorized users - define('PRE_AUTHORIZE_NEW_DEVICES', false); - - // Max number of devices pre-authorized for user, you can pre-authorize more manually - define('PRE_AUTHORIZE_MAX_DEVICES', 5); - - -/********************************************************************************** - * Select StateMachine mechanism - * - * FILE => FileStateMachine, default - * SQL => SqlStateMachine - */ - define('STATE_MACHINE', 'FILE'); - -/********************************************************************************** - * Default FileStateMachine settings - */ - define('STATE_DIR', '/var/lib/z-push/'); - - -/********************************************************************************** - * Optional SqlStateMachine settings - * - * DSN: formatted PDO connection string - * mysql:host=xxx;port=xxx;dbname=xxx - * DON'T FORGET TO INSTALL THE PHP-DRIVER PACKAGE!!! - * USER: username to DB - * PASSWORD: password to DB - * OPTIONS: array with options needed - */ - define('STATE_SQL_DSN', ''); - define('STATE_SQL_USER', ''); - define('STATE_SQL_PASSWORD', ''); - define('STATE_SQL_OPTIONS', serialize(array(PDO::ATTR_PERSISTENT => true))); - - -/********************************************************************************** - * Logging settings - * Possible LOGLEVEL and LOGUSERLEVEL values are: - * LOGLEVEL_OFF - no logging - * LOGLEVEL_FATAL - log only critical errors - * LOGLEVEL_ERROR - logs events which might require corrective actions - * LOGLEVEL_WARN - might lead to an error or require corrective actions in the future - * LOGLEVEL_INFO - usually completed actions - * LOGLEVEL_DEBUG - debugging information, typically only meaningful to developers - * LOGLEVEL_WBXML - also prints the WBXML sent to/from the device - * LOGLEVEL_DEVICEID - also prints the device id for every log entry - * LOGLEVEL_WBXMLSTACK - also prints the contents of WBXML stack - * - * The verbosity increases from top to bottom. More verbose levels include less verbose - * ones, e.g. setting to LOGLEVEL_DEBUG will also output LOGLEVEL_FATAL, LOGLEVEL_ERROR, - * LOGLEVEL_WARN and LOGLEVEL_INFO level entries. - */ - define('LOGFILEDIR', '/var/log/z-push/'); - define('LOGFILE', LOGFILEDIR . 'z-push.log'); - define('LOGERRORFILE', LOGFILEDIR . 'z-push-error.log'); - define('LOGLEVEL', LOGLEVEL_INFO); - define('LOGAUTHFAIL', false); - - - // To save e.g. WBXML data only for selected users, add the usernames to the array - // The data will be saved into a dedicated file per user in the LOGFILEDIR - // Users have to be encapusulated in quotes, several users are comma separated, like: - // $specialLogUsers = array('info@domain.com', 'myusername'); - define('LOGUSERLEVEL', LOGLEVEL_DEVICEID); - $specialLogUsers = array(); - - // If you want to disable log to file, and log to syslog instead - define('LOG_SYSLOG_ENABLED', false); - // false will log to local syslog, otherwise put the remote syslog IP here - define('LOG_SYSLOG_HOST', false); - // Syslog port - define('LOG_SYSLOG_PORT', 514); - // Program showed in the syslog. Useful if you have more than one instance login to the same syslog - define('LOG_SYSLOG_PROGRAM', '[z-push]'); - - - define('LOG_MEMORY_PROFILER', true); - define('LOG_MEMORY_PROFILER_FILE', '/var/log/z-push/memory_profile'); - -/********************************************************************************** - * Mobile settings - */ - // Device Provisioning - define('PROVISIONING', true); - - // This option allows the 'loose enforcement' of the provisioning policies for older - // devices which don't support provisioning (like WM 5 and HTC Android Mail) - dw2412 contribution - // false (default) - Enforce provisioning for all devices - // true - allow older devices, but enforce policies on devices which support it - define('LOOSE_PROVISIONING', false); - - // Default conflict preference - // Some devices allow to set if the server or PIM (mobile) - // should win in case of a synchronization conflict - // SYNC_CONFLICT_OVERWRITE_SERVER - Server is overwritten, PIM wins - // SYNC_CONFLICT_OVERWRITE_PIM - PIM is overwritten, Server wins (default) - define('SYNC_CONFLICT_DEFAULT', SYNC_CONFLICT_OVERWRITE_PIM); - - // Global limitation of items to be synchronized - // The mobile can define a sync back period for calendar and email items - // For large stores with many items the time period could be limited to a max value - // If the mobile transmits a wider time period, the defined max value is used - // Applicable values: - // SYNC_FILTERTYPE_ALL (default, no limitation) - // SYNC_FILTERTYPE_1DAY, SYNC_FILTERTYPE_3DAYS, SYNC_FILTERTYPE_1WEEK, SYNC_FILTERTYPE_2WEEKS, - // SYNC_FILTERTYPE_1MONTH, SYNC_FILTERTYPE_3MONTHS, SYNC_FILTERTYPE_6MONTHS - define('SYNC_FILTERTIME_MAX', SYNC_FILTERTYPE_ALL); - - // Interval in seconds before checking if there are changes on the server when in Ping. - // It means the highest time span before a change is pushed to a mobile. Set it to - // a higher value if you have a high load on the server. - define('PING_INTERVAL', 30); - - // Interval in seconds to force a re-check of potentially missed notifications when - // using a changes sink. Default are 300 seconds (every 5 min). - // This can also be disabled by setting it to false - define('SINK_FORCERECHECK', 300); - - // Set the fileas (save as) order for contacts in the webaccess/webapp/outlook. - // It will only affect new/modified contacts on the mobile which then are synced to the server. - // Possible values are: - // SYNC_FILEAS_FIRSTLAST - fileas will be "Firstname Middlename Lastname" - // SYNC_FILEAS_LASTFIRST - fileas will be "Lastname, Firstname Middlename" - // SYNC_FILEAS_COMPANYONLY - fileas will be "Company" - // SYNC_FILEAS_COMPANYLAST - fileas will be "Company (Lastname, Firstname Middlename)" - // SYNC_FILEAS_COMPANYFIRST - fileas will be "Company (Firstname Middlename Lastname)" - // SYNC_FILEAS_LASTCOMPANY - fileas will be "Lastname, Firstname Middlename (Company)" - // SYNC_FILEAS_FIRSTCOMPANY - fileas will be "Firstname Middlename Lastname (Company)" - // The company-fileas will only be set if a contact has a company set. If one of - // company-fileas is selected and a contact doesn't have a company set, it will default - // to SYNC_FILEAS_FIRSTLAST or SYNC_FILEAS_LASTFIRST (depending on if last or first - // option is selected for company). - // If SYNC_FILEAS_COMPANYONLY is selected and company of the contact is not set - // SYNC_FILEAS_LASTFIRST will be used - define('FILEAS_ORDER', SYNC_FILEAS_LASTFIRST); - - // Amount of items to be synchronized per request - // Normally this value is requested by the mobile. Common values are 5, 25, 50 or 100. - // Exporting too much items can cause mobile timeout on busy systems. - // Z-Push will use the lowest value, either set here or by the mobile. - // default: 100 - value used if mobile does not limit amount of items - define('SYNC_MAX_ITEMS', 100); - - // The devices usually send a list of supported properties for calendar and contact - // items. If a device does not includes such a supported property in Sync request, - // it means the property's value will be deleted on the server. - // However some devices do not send a list of supported properties. It is then impossible - // to tell if a property was deleted or it was not set at all if it does not appear in Sync. - // This parameter defines Z-Push behaviour during Sync if a device does not issue a list with - // supported properties. - // See also https://jira.zarafa.com/browse/ZP-302. - // Possible values: - // false - do not unset properties which are not sent during Sync (default) - // true - unset properties which are not sent during Sync - define('UNSET_UNDEFINED_PROPERTIES', false); - - // ActiveSync specifies that a contact photo may not exceed 48 KB. This value is checked - // in the semantic sanity checks and contacts with larger photos are not synchronized. - // This limitation is not being followed by the ActiveSync clients which set much bigger - // contact photos. You can override the default value of the max photo size. - // default: 5242880 - 5 MB default max photo size in bytes - define('SYNC_CONTACTS_MAXPICTURESIZE', 5242880); - - // Over the WebserviceUsers command it is possible to retrieve a list of all - // known devices and users on this Z-Push system. The authenticated user needs to have - // admin rights and a public folder must exist. - // In multicompany environments this enable an admin user of any company to retrieve - // this full list, so this feature is disabled by default. Enable with care. - define('ALLOW_WEBSERVICE_USERS_ACCESS', false); - - // Users with many folders can use the 'partial foldersync' feature, where the server - // actively stops processing the folder list if it takes too long. Other requests are - // then redirected to the FolderSync to synchronize the remaining items. - // Device compatibility for this procedure is not fully understood. - // NOTE: THIS IS AN EXPERIMENTAL FEATURE WHICH COULD PREVENT YOUR MOBILES FROM SYNCHRONIZING. - define('USE_PARTIAL_FOLDERSYNC', false); - -/********************************************************************************** - * Backend settings - */ - // the backend data provider - define('BACKEND_PROVIDER', ''); - - // top collector backend class name - // Default is: TopCollector - // Options: ["TopCollector", "TopCollectorRedis"] - define('TOP_COLLECTOR_BACKEND', 'TopCollector'); - - // ping tracking backend class name - // Default is: PingTracking - // Options: ["PingTracking", "PingTrackingRedis"] - define('PING_TRACKING_BACKEND', 'PingTracking'); - - // loop detection backend class name - // Default is: LoopDetection - // Options: ["LoopDetection", "LoopDetectionRedis"] - define('LOOP_DETECTION_BACKEND', 'LoopDetection'); - - // If using the Redis backends (for top, ping and lookp) make sure to set this values as necessary - define('IPC_REDIS_IP', '127.0.0.1'); - define('IPC_REDIS_PORT', 6379); - // Database name/index in Redis: 0 by default - // NOTE: this database must be exclusive for z-push, since its content will be ERASED. You are warned. - define('IPC_REDIS_DATABASE', 0); - -/********************************************************************************** - * Search provider settings - * - * Alternative backend to perform SEARCH requests (GAL search) - * By default the main Backend defines the preferred search functionality. - * If set, the Search Provider will always be preferred. - * Use 'BackendSearchLDAP' to search in a LDAP directory (see backend/searchldap/config.php) - */ - define('SEARCH_PROVIDER', ''); - // Time in seconds for the server search. Setting it too high might result in timeout. - // Setting it too low might not return all results. Default is 10. - define('SEARCH_WAIT', 10); - // The maximum number of results to send to the client. Setting it too high - // might result in timeout. Default is 10. - define('SEARCH_MAXRESULTS', 10); - - -/********************************************************************************** - * Synchronize additional folders to all mobiles - * - * With this feature, special folders can be synchronized to all mobiles. - * This is useful for e.g. global company contacts. - * - * This feature is supported only by certain devices, like iPhones. - * Check the compatibility list for supported devices: - * http://z-push.sf.net/compatibility - * - * To synchronize a folder, add a section setting all parameters as below: - * store: the ressource where the folder is located. - * Zarafa users use 'SYSTEM' for the 'Public Folder' - * folderid: folder id of the folder to be synchronized - * name: name to be displayed on the mobile device - * type: supported types are: - * SYNC_FOLDER_TYPE_USER_CONTACT - * SYNC_FOLDER_TYPE_USER_APPOINTMENT - * SYNC_FOLDER_TYPE_USER_TASK - * SYNC_FOLDER_TYPE_USER_MAIL - * - * Additional notes: - * - on Zarafa systems use backend/zarafa/listfolders.php script to get a list - * of available folders - * - * - all Z-Push users must have full writing permissions (secretary rights) so - * the configured folders can be synchronized to the mobile - * - * - this feature is only partly suitable for multi-tenancy environments, - * as ALL users from ALL tenents need access to the configured store & folder. - * When configuring a public folder, this will cause problems, as each user has - * a different public folder in his tenant, so the folder are not available. - - * - changing this configuration could cause HIGH LOAD on the system, as all - * connected devices will be updated and load the data contained in the - * added/modified folders. - */ - - $additionalFolders = array( - // demo entry for the synchronization of contacts from the public folder. - // uncomment (remove '/*' '*/') and fill in the folderid -/* - array( - 'store' => "SYSTEM", - 'folderid' => "", - 'name' => "Public Contacts", - 'type' => SYNC_FOLDER_TYPE_USER_CONTACT, - ), -*/ - ); diff --git a/sources/docker/README.md b/sources/docker/README.md deleted file mode 100644 index 66e660b..0000000 --- a/sources/docker/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Docker Images - -You can run a Z-Push server using Docker containers. That is really usefull for developing, but it also can be used in production servers. - - -Here are the basic instructions for a Nginx+PHP-FPM deployment. Feel free to contribute your Apache or other server approach. - - -## Using Docker Composer (In progress) - - -### Create and build basic images - - docker-compose -f basic.yml up - - - - - -## Manual method - - -### Build a PHP-FPM image - - cd php-fpm - docker build -t fmbiete/centos_zpush_php_fpm . - - -### Build a NGINX image - - cd nginx - docker build -t fmbiete/centos_zpush_nginx . - -**NOTE**: this includes a SSL self-signed certificate (2048 bytes - valid until 2025), but it's intended only for development or testing uses. In production replace it with a real one. - - -### Create MariaDB container (optional for SQLStateMachine) - - docker run --name zpush_mariadb -e MYSQL_ROOT_PASSWORD=root_password -e MYSQL_USER=user_name -e MYSQL_PASSWORD=user_password -e MYSQL_DATABASE=database -v mariadb_lib:/var/lib/mysql -p3306:3306 -d fbiete/centos_epel_mariadb:10 - -**TODO**: Replace *mariadb_lib* with the full path when you will store the database files -**TODO**: If using selinux remember to change the context type for *mariadb_lib* -**TODO**: Replace *root_password*, *user_name*, *user_password*, *database* with the right values - -#### Load database schema - - mysql -u root -proot_password database -h 127.0.0.1 < sql/mysql.sql - - -### Create Redis container (optional for TopCollectorRedis, LoopDetectionRedis or PingTrackingRedis) - - docker run --name zpush_redis -v redis_data:/data -p 6379:6379 -d fbiete/centos_epel_redis:2.8 - -**TODO**: Replace *redis_data* with the full path when you will store the database files -**TODO**: If using selinux remember to change the context type for *redis_data* - -### Create PHP-FPM container - - docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push fmbiete/centos_zpush_php_fpm - -#### With MariaDB - - docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push --link zpush_mariadb:zpushmariadb fmbiete/centos_zpush_php_fpm - -#### With Redis - - docker run -d --name zpush_php_fpm -v zpush_repo:/var/www/z-push --link zpush_redis:zpushredis fmbiete/centos_zpush_php_fpm - -**TODO**: Replace *zpush_repo* with the full path to Z-Push code -**TODO**: Remember to zpushmariadb and zpushredis as server name in the config for MariaDB and Redis - - -### Create NGINX container - - docker run -d --name zpush_nginx -v zpush_repo:/var/www/z-push --link zpush_php_fpm:zpushphpfpm -p 443:443 fmbiete/centos_zpush_nginx - -**TODO**: Replace *zpush_repo* with the full path to Z-Push code - - -### Stop containers - - docker stop zpush_nginx - docker stop zpush_php_fpm - docker stop zpush_mariadb - docker stop zpush_redis - - -### Start containers - - docker start zpush_mariadb - docker start zpush_redis - docker start zpush_php_fpm - docker start zpush_nginx - - -### Remove containers - - docker rm zpush_nginx - docker rm zpush_php_fpm - docker rm zpush_mariadb - docker rm zpush_redis - -**NOTE**: The order of the containers in the operation is important \ No newline at end of file diff --git a/sources/docker/basic.yml b/sources/docker/basic.yml deleted file mode 100644 index 8b92139..0000000 --- a/sources/docker/basic.yml +++ /dev/null @@ -1,13 +0,0 @@ -phpfpm: - build: php-fpm/ - volumes: - - ..:/var/www/z-push:Z -nginx: - build: nginx/ - volumes_from: - - phpfpm - links: - - phpfpm:zpushphpfpm - ports: - - "80:80" - - "443:443" diff --git a/sources/docker/nginx/Dockerfile b/sources/docker/nginx/Dockerfile deleted file mode 100644 index 8b3248d..0000000 --- a/sources/docker/nginx/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM fbiete/centos_epel_nginx:1.8 -MAINTAINER Francisco Miguel Biete - -RUN mkdir /var/www /var/www/z-push /etc/ssl/nginx \ -&& chown -R nginx:nginx /var/www - -COPY localhost.crt /etc/ssl/nginx/localhost.crt -COPY localhost.key /etc/ssl/nginx/localhost.key - -COPY nginx.conf /etc/nginx/ - -VOLUME /var/www/z-push - -CMD [ "nginx", "-g", "daemon off;" ] - - diff --git a/sources/docker/nginx/localhost.crt b/sources/docker/nginx/localhost.crt deleted file mode 100644 index 66a95e4..0000000 --- a/sources/docker/nginx/localhost.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfzCCAmegAwIBAgIJAMNks+T7RrPhMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV -BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg -Q29tcGFueSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAzMTYwNzQwMzZa -Fw0yNTAzMTMwNzQwMzZaMFYxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0 -IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxEjAQBgNVBAMMCWxv -Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhw96+bbz0I -5tPNAUJo/TG3Qi8RzkHyeGeg+dbMckS2Y3gcaa4E8nsnuKisbmHZu6Zgc7P8sr9d -V6qTsz07aJuy5pdt3+y9oiHPd05EZ4aZSXbfdGLAwr94D6R3AI6zv6lA+cCAHIIu -A4tgOmjtykre632dDDVLyDyxVTekn28q6ag+6vDnj9gyABsvER7WsJpi1Af6HxH2 -/tM1EtCKam5SNVy9+lQs3/pXk8r8kKvKVyrewhTzy4F8IRVi0vXtcW7wtkDwO1Ti -+CN1C1ETQZ2jfTk7Z9xGaFbS5cIEbHH3AmBgJjT396pUBQEqQVHBsHxvmhFhKMBi -ejvFbTYFz8cCAwEAAaNQME4wHQYDVR0OBBYEFECo2oRuFvk9sUOwRzZ+BeH48YJR -MB8GA1UdIwQYMBaAFECo2oRuFvk9sUOwRzZ+BeH48YJRMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQELBQADggEBAK5ZATJ3Oh+0bXXdPMSTCZDgsGYpm5/BrUiAbqXX -mMRWyx6nUF6QqDu6Fku+Jgo0RwhXz7VfwI1JNXWvDsoEnjCbWJ2+njH08qBn9Xex -wtxL+kwnjXVeZgskUa9sAP+nr2gyzhjyRFjx1W3gZQeJ9VY2pDKLpW2NTkUOEhOH -YzLSUzVlXdQUauiYglzqip7dUId5VeDXHC8merB7Iq8h7QxD0WVHyjlgSjWEH8Gq -MDaY+n6CyPXkmusNJlQoWB/CJcLfr3tSVvaqmZ49K3OZph3DCKiGnSqqFi5OqKLg -YkcculYQGwfUkqZPqTb++MTsKkuaQPk4UDbPYAYhHJnBT+A= ------END CERTIFICATE----- diff --git a/sources/docker/nginx/localhost.key b/sources/docker/nginx/localhost.key deleted file mode 100644 index ede0d56..0000000 --- a/sources/docker/nginx/localhost.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAuHD3r5tvPQjm080BQmj9MbdCLxHOQfJ4Z6D51sxyRLZjeBxp -rgTyeye4qKxuYdm7pmBzs/yyv11XqpOzPTtom7Lml23f7L2iIc93TkRnhplJdt90 -YsDCv3gPpHcAjrO/qUD5wIAcgi4Di2A6aO3KSt7rfZ0MNUvIPLFVN6SfbyrpqD7q -8OeP2DIAGy8RHtawmmLUB/ofEfb+0zUS0IpqblI1XL36VCzf+leTyvyQq8pXKt7C -FPPLgXwhFWLS9e1xbvC2QPA7VOL4I3ULURNBnaN9OTtn3EZoVtLlwgRscfcCYGAm -NPf3qlQFASpBUcGwfG+aEWEowGJ6O8VtNgXPxwIDAQABAoIBACbhMWUkN9u+36Gw -Kl7McOsk/V+cukTujvERXvknmcLgS7GLE7/qLQ9G/UcZKh+YXVUiKeG8GBX84DkF -75etyUxg9vje4YAvLVlBOZ4XD1exQmo7inYyuhrQfUOnDkgGnhVYrA0nNFtAxeCA -hW+PCMClozCUhXlKo0gf/Z3AJxewsVm4x+G73aU9t/yxm6A4Z2OEYGfD240uhfC/ -fEVNpL3xs2TPP4W90yw+Q+oZ0g/hmXKgFQ1PujD5+hGtiRLQ3v1Apb1LhPTz3pPg -OK/VSWbDXBSESqU5aFfk3NP7KSnVfxkUVQk9MAnofq2p3BrPv0ovjgliyMHOV88/ -6xdkIBkCgYEA5QFSH63n9WuQz2ZHR7YrU/i8ATrQ9ex1RawLAVZA+YN+GC2/Kd+c -GDyB1ed2v7o/DzCbiVE9WGhCLUE2WDp8J+MVIbgVDYnCx6wXLaMtkzSMtQMT6UV4 -+ks6FqvrjmzwdvtrX31XfHCvsGkPhHKeoUU0SnIwvbyxq2sNBdH0E2UCgYEAzi7Y -KIQ/0ryekr6Mf8pAOaprxJX4ykArvTTGwzVazaumTNW0AArEnKNdebB+kVVko4h0 -2kUPbJTNwGTxJiuu7HJGE1vla++wlpyPryqghVPIgwVIQOjkV3guq8BbOPyB/xtv -9yVJZqeJc0Jj+ZV7Cm+kQ514F3e1cpidYUUjQbsCgYA+Jj+dbVr4Vfr07nMF2UCl -B2oug0HWnBevkuNht4DmtnLwKOoqeQ8p3LH31Vt66RbYDn8Ho06cwZ7EHWCcTTMI -uC4x+n1sMSj1e5TGw/RIcQiGz5EFy97rPqNDJ+FDw/j2sYEQZznpAcQMgla9wUWf -yuJIGfl0ZNNrDCB6peIxqQKBgQCKTo4dj6koefJ9SWkCB+/RPuqPsnJzaVxtzUtP -gyjoMi6Z9/iI1rBQyp1XlfcxEnEx6cVI7W6NTbw/RPcmvcLXRUiQj+Jz5xMz1M3l -mNiY1zz39sEjGZaivjHAcIZA0dF6CTOwO8jjHZtsP6rEr2sb8wvjd2wpgdmrh4h6 -yV//JQKBgQDL4q0becCvRcC8HYPt5LkxWHLvvOlP4Z8x19DzMw0xhvhqvtk7cuxy -ioQckj/9Qa5icYqXGUY1eg2wSMe4mJdCbosXFDdPi3pW7eXJwmQmVCwHx0INDq6z -Xn5hG0ZRioytwV8aqbq8k1PLHm6mmY71dH+riou2JwFD2py7RqBJqQ== ------END RSA PRIVATE KEY----- diff --git a/sources/docker/nginx/nginx.conf b/sources/docker/nginx/nginx.conf deleted file mode 100644 index d38c148..0000000 --- a/sources/docker/nginx/nginx.conf +++ /dev/null @@ -1,91 +0,0 @@ -user nginx; -worker_processes 1; - -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - sendfile on; - - keepalive_timeout 65; - - # max_execution_time is 900 - proxy_read_timeout 910; - - # Disable SSLv3 to protect us from POODLE & company - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - - server { - listen *:443; - - server_name localhost; - - ssl on; - ssl_certificate /etc/ssl/nginx/localhost.crt; - ssl_certificate_key /etc/ssl/nginx/localhost.key; - - root /var/www/z-push; - index index.php; - - error_log /var/log/nginx/zpush-error.log; - access_log /var/log/nginx/zpush-access.log; - - # Attachments 20MB max - client_max_body_size 20m; - client_body_buffer_size 128k; - - location / { - try_files $uri $uri/ index.php; - } - - location /Microsoft-Server-ActiveSync { - rewrite ^(.*)$ /index.php last; - } - - location ~ .php$ { - include /etc/nginx/fastcgi_params; - fastcgi_index index.php; - fastcgi_param HTTPS on; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_pass zpushphpfpm:9000; - # PHP max_execution_time is set to 900 - fastcgi_read_timeout 910; - } - - - # You don't really need all of the next, but I have them in my server - location = /robots.txt { - access_log off; - log_not_found off; - } - - location = /favicon.ico { - return 204; - access_log off; - log_not_found off; - } - - location ~ ^\. { - access_log on; - log_not_found off; - deny all; - } - - location ~ ~$ { - access_log off; - log_not_found off; - deny all; - } - } -} \ No newline at end of file diff --git a/sources/docker/php-fpm/Dockerfile b/sources/docker/php-fpm/Dockerfile deleted file mode 100644 index 3409aa8..0000000 --- a/sources/docker/php-fpm/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM fbiete/centos_epel_php_fpm:5.6 -MAINTAINER Francisco Miguel Biete - -RUN yum clean all \ -&& yum install -y --enablerepo=remi-php56 \ -php-pecl-memprof \ -mailcap \ -&& yum clean all \ -&& cd /usr/local/src \ -&& curl -LSs https://gitlab.com/davical-project/awl/repository/archive.tar.gz | tar xz \ -&& echo "include_path=.:/usr/share/pear:/usr/share/php:/usr/local/src/awl.git/inc" >> /etc/php.ini \ -&& sed -i 's/max_execution_time = 30/max_execution_time = 900/g' /etc/php.ini \ -&& sed -i 's/max_input_time = 60/max_input_time = 300/g' /etc/php.ini \ -&& sed -i 's/post_max_size = 8M/post_max_size = 20M/g' /etc/php.ini \ -&& sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 20M/g' /etc/php.ini \ -&& mkdir /var/log/z-push /var/lib/z-push \ -&& chown -R apache:apache /var/log/z-push /var/lib/z-push - -VOLUME /var/www/z-push - - diff --git a/sources/include/Auth/SASL.php b/sources/include/Auth/SASL.php deleted file mode 100755 index 22acd8b..0000000 --- a/sources/include/Auth/SASL.php +++ /dev/null @@ -1,148 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Client implementation of various SASL mechanisms -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Auth_SASL-1.0.6.tgz - * - * - */ - -//require_once('PEAR.php'); - -class Auth_SASL -{ - /** - * Factory class. Returns an object of the request - * type. - * - * @param string $type One of: Anonymous - * Plain - * CramMD5 - * DigestMD5 - * SCRAM-* (any mechanism of the SCRAM family) - * Types are not case sensitive - */ - function &factory($type) - { - switch (strtolower($type)) { - case 'anonymous': - $filename = 'include/Auth/SASL/Anonymous.php'; - $classname = 'Auth_SASL_Anonymous'; - break; - - case 'login': - $filename = 'include/Auth/SASL/Login.php'; - $classname = 'Auth_SASL_Login'; - break; - - case 'plain': - $filename = 'include/Auth/SASL/Plain.php'; - $classname = 'Auth_SASL_Plain'; - break; - - case 'external': - $filename = 'include/Auth/SASL/External.php'; - $classname = 'Auth_SASL_External'; - break; - - case 'crammd5': - // $msg = 'Deprecated mechanism name. Use IANA-registered name: CRAM-MD5.'; - // trigger_error($msg, E_USER_DEPRECATED); - case 'cram-md5': - $filename = 'include/Auth/SASL/CramMD5.php'; - $classname = 'Auth_SASL_CramMD5'; - break; - - case 'digestmd5': - // $msg = 'Deprecated mechanism name. Use IANA-registered name: DIGEST-MD5.'; - // trigger_error($msg, E_USER_DEPRECATED); - case 'digest-md5': - // $msg = 'DIGEST-MD5 is a deprecated SASL mechanism as per RFC-6331. Using it could be a security risk.'; - // trigger_error($msg, E_USER_NOTICE); - $filename = 'include/Auth/SASL/DigestMD5.php'; - $classname = 'Auth_SASL_DigestMD5'; - break; - - default: - $scram = '/^SCRAM-(.{1,9})$/i'; - if (preg_match($scram, $type, $matches)) - { - $hash = $matches[1]; - $filename = 'include/Auth/SASL/SCRAM.php'; - $classname = 'Auth_SASL_SCRAM'; - $parameter = $hash; - break; - } - return Auth_SASL::raiseError('Invalid SASL mechanism type'); - break; - } - - require_once($filename); - if (isset($parameter)) - $obj = new $classname($parameter); - else - $obj = new $classname(); - return $obj; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Auth_SASL error: ". $message); - return false; - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/Anonymous.php b/sources/include/Auth/SASL/Anonymous.php deleted file mode 100755 index ac0f0e9..0000000 --- a/sources/include/Auth/SASL/Anonymous.php +++ /dev/null @@ -1,68 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Implmentation of ANONYMOUS SASL mechanism -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_Anonymous extends Auth_SASL_Common -{ - /** - * Not much to do here except return the token supplied. - * No encoding, hashing or encryption takes place for this - * mechanism, simply one of: - * o An email address - * o An opaque string not containing "@" that can be interpreted - * by the sysadmin - * o Nothing - * - * We could have some logic here for the second option, but this - * would by no means create something interpretable. - * - * @param string $token Optional email address or string to provide - * as trace information. - * @return string The unaltered input token - */ - function getResponse($token = '') - { - return $token; - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/Common.php b/sources/include/Auth/SASL/Common.php deleted file mode 100755 index e641cbe..0000000 --- a/sources/include/Auth/SASL/Common.php +++ /dev/null @@ -1,128 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Common functionality to SASL mechanisms -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Auth_SASL-1.0.6.tgz - * - * - */ - -class Auth_SASL_Common -{ - /** - * Function which implements HMAC MD5 digest - * - * @param string $key The secret key - * @param string $data The data to hash - * @param bool $raw_output Whether the digest is returned in binary or hexadecimal format. - * - * @return string The HMAC-MD5 digest - */ - function _HMAC_MD5($key, $data, $raw_output = FALSE) - { - if (strlen($key) > 64) { - $key = pack('H32', md5($key)); - } - - if (strlen($key) < 64) { - $key = str_pad($key, 64, chr(0)); - } - - $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64); - $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64); - - $inner = pack('H32', md5($k_ipad . $data)); - $digest = md5($k_opad . $inner, $raw_output); - - return $digest; - } - - /** - * Function which implements HMAC-SHA-1 digest - * - * @param string $key The secret key - * @param string $data The data to hash - * @param bool $raw_output Whether the digest is returned in binary or hexadecimal format. - * @return string The HMAC-SHA-1 digest - * @author Jehan - * @access protected - */ - protected function _HMAC_SHA1($key, $data, $raw_output = FALSE) - { - if (strlen($key) > 64) { - $key = sha1($key, TRUE); - } - - if (strlen($key) < 64) { - $key = str_pad($key, 64, chr(0)); - } - - $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64); - $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64); - - $inner = pack('H40', sha1($k_ipad . $data)); - $digest = sha1($k_opad . $inner, $raw_output); - - return $digest; - } - -/** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "SCRAM error: ". $message); - return false; - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/CramMD5.php b/sources/include/Auth/SASL/CramMD5.php deleted file mode 100755 index 97abcba..0000000 --- a/sources/include/Auth/SASL/CramMD5.php +++ /dev/null @@ -1,65 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Implmentation of CRAM-MD5 SASL mechanism -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_CramMD5 extends Auth_SASL_Common -{ - /** - * Implements the CRAM-MD5 SASL mechanism - * This DOES NOT base64 encode the return value, - * you will need to do that yourself. - * - * @param string $user Username - * @param string $pass Password - * @param string $challenge The challenge supplied by the server. - * this should be already base64_decoded. - * - * @return string The string to pass back to the server, of the form - * " ". This is NOT base64_encoded. - */ - function getResponse($user, $pass, $challenge) - { - return $user . ' ' . $this->_HMAC_MD5($pass, $challenge); - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/DigestMD5.php b/sources/include/Auth/SASL/DigestMD5.php deleted file mode 100755 index 2065756..0000000 --- a/sources/include/Auth/SASL/DigestMD5.php +++ /dev/null @@ -1,194 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Implmentation of DIGEST-MD5 SASL mechanism -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_DigestMD5 extends Auth_SASL_Common -{ - /** - * Provides the (main) client response for DIGEST-MD5 - * requires a few extra parameters than the other - * mechanisms, which are unavoidable. - * - * @param string $authcid Authentication id (username) - * @param string $pass Password - * @param string $challenge The digest challenge sent by the server - * @param string $hostname The hostname of the machine you're connecting to - * @param string $service The servicename (eg. imap, pop, acap etc) - * @param string $authzid Authorization id (username to proxy as) - * @return string The digest response (NOT base64 encoded) - * @access public - */ - function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '') - { - $challenge = $this->_parseChallenge($challenge); - $authzid_string = ''; - if ($authzid != '') { - $authzid_string = ',authzid="' . $authzid . '"'; - } - - if (!empty($challenge)) { - $cnonce = $this->_getCnonce(); - $digest_uri = sprintf('%s/%s', $service, $hostname); - $response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid); - - if ($challenge['realm']) { - return sprintf('username="%s",realm="%s"' . $authzid_string . -',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']); - } else { - return sprintf('username="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc=00000001,qop=auth,digest-uri="%s",response=%s,maxbuf=%d', $authcid, $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']); - } - } else { - return $this->raiseError('Invalid digest challenge'); - } - } - - /** - * Parses and verifies the digest challenge* - * - * @param string $challenge The digest challenge - * @return array The parsed challenge as an assoc - * array in the form "directive => value". - * @access private - */ - function _parseChallenge($challenge) - { - $tokens = array(); - while (preg_match('/^([a-z-]+)=("[^"]+(? | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Implmentation of EXTERNAL SASL mechanism -* -* @author Christoph Schulz -* @access public -* @version 1.0.3 -* @package Auth_SASL -*/ - -class Auth_SASL_External extends Auth_SASL_Common -{ - /** - * Returns EXTERNAL response - * - * @param string $authcid Authentication id (username) - * @param string $pass Password - * @param string $authzid Autorization id - * @return string EXTERNAL Response - */ - function getResponse($authcid, $pass, $authzid = '') - { - return $authzid; - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/Login.php b/sources/include/Auth/SASL/Login.php deleted file mode 100755 index b6cec5e..0000000 --- a/sources/include/Auth/SASL/Login.php +++ /dev/null @@ -1,62 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* This is technically not a SASL mechanism, however -* it's used by Net_Sieve, Net_Cyrus and potentially -* other protocols , so here is a good place to abstract -* it. -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_Login extends Auth_SASL_Common -{ - /** - * Pseudo SASL LOGIN mechanism - * - * @param string $user Username - * @param string $pass Password - * @return string LOGIN string - */ - function getResponse($user, $pass) - { - return sprintf('LOGIN %s %s', $user, $pass); - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/Plain.php b/sources/include/Auth/SASL/Plain.php deleted file mode 100755 index 03b483b..0000000 --- a/sources/include/Auth/SASL/Plain.php +++ /dev/null @@ -1,60 +0,0 @@ - | -// +-----------------------------------------------------------------------+ -// -// $Id$ - -/** -* Implmentation of PLAIN SASL mechanism -* -* @author Richard Heyes -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_Plain extends Auth_SASL_Common -{ - /** - * Returns PLAIN response - * - * @param string $authcid Authentication id (username) - * @param string $pass Password - * @param string $authzid Autorization id - * @return string PLAIN Response - */ - function getResponse($authcid, $pass, $authzid = '') - { - return $authzid . chr(0) . $authcid . chr(0) . $pass; - } -} \ No newline at end of file diff --git a/sources/include/Auth/SASL/SCRAM.php b/sources/include/Auth/SASL/SCRAM.php deleted file mode 100644 index d9e1ef1..0000000 --- a/sources/include/Auth/SASL/SCRAM.php +++ /dev/null @@ -1,301 +0,0 @@ - -* @access public -* @version 1.0 -* @package Auth_SASL -*/ - -class Auth_SASL_SCRAM extends Auth_SASL_Common -{ - /** - * Construct a SCRAM-H client where 'H' is a cryptographic hash function. - * - * @param string $hash The name cryptographic hash function 'H' as registered by IANA in the "Hash Function Textual - * Names" registry. - * @link http://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml "Hash Function Textual - * Names" - * format of core PHP hash function. - * @access public - */ - function __construct($hash) - { - // Though I could be strict, I will actually also accept the naming used in the PHP core hash framework. - // For instance "sha1" is accepted, while the registered hash name should be "SHA-1". - $hash = strtolower($hash); - $hashes = array('md2' => 'md2', - 'md5' => 'md5', - 'sha-1' => 'sha1', - 'sha1' => 'sha1', - 'sha-224' > 'sha224', - 'sha224' > 'sha224', - 'sha-256' => 'sha256', - 'sha256' => 'sha256', - 'sha-384' => 'sha384', - 'sha384' => 'sha384', - 'sha-512' => 'sha512', - 'sha512' => 'sha512'); - if (function_exists('hash_hmac') && isset($hashes[$hash])) - { - $this->hash = create_function('$data', 'return hash("' . $hashes[$hash] . '", $data, TRUE);'); - $this->hmac = create_function('$key,$str,$raw', 'return hash_hmac("' . $hashes[$hash] . '", $str, $key, $raw);'); - } - elseif ($hash == 'md5') - { - $this->hash = create_function('$data', 'return md5($data, true);'); - $this->hmac = array($this, '_HMAC_MD5'); - } - elseif (in_array($hash, array('sha1', 'sha-1'))) - { - $this->hash = create_function('$data', 'return sha1($data, true);'); - $this->hmac = array($this, '_HMAC_SHA1'); - } - else - return $this->raiseError('Invalid SASL mechanism type'); - } - - /** - * Provides the (main) client response for SCRAM-H. - * - * @param string $authcid Authentication id (username) - * @param string $pass Password - * @param string $challenge The challenge sent by the server. - * If the challenge is NULL or an empty string, the result will be the "initial response". - * @param string $authzid Authorization id (username to proxy as) - * @return string|false The response (binary, NOT base64 encoded) - * @access public - */ - public function getResponse($authcid, $pass, $challenge = NULL, $authzid = NULL) - { - $authcid = $this->_formatName($authcid); - if (empty($authcid)) - { - return false; - } - if (!empty($authzid)) - { - $authzid = $this->_formatName($authzid); - if (empty($authzid)) - { - return false; - } - } - - if (empty($challenge)) - { - return $this->_generateInitialResponse($authcid, $authzid); - } - else - { - return $this->_generateResponse($challenge, $pass); - } - - } - - /** - * Prepare a name for inclusion in a SCRAM response. - * - * @param string $username a name to be prepared. - * @return string the reformated name. - * @access private - */ - private function _formatName($username) - { - // TODO: prepare through the SASLprep profile of the stringprep algorithm. - // See RFC-4013. - - $username = str_replace('=', '=3D', $username); - $username = str_replace(',', '=2C', $username); - return $username; - } - - /** - * Generate the initial response which can be either sent directly in the first message or as a response to an empty - * server challenge. - * - * @param string $authcid Prepared authentication identity. - * @param string $authzid Prepared authorization identity. - * @return string The SCRAM response to send. - * @access private - */ - private function _generateInitialResponse($authcid, $authzid) - { - $init_rep = ''; - $gs2_cbind_flag = 'n,'; // TODO: support channel binding. - $this->gs2_header = $gs2_cbind_flag . (!empty($authzid)? 'a=' . $authzid : '') . ','; - - // I must generate a client nonce and "save" it for later comparison on second response. - $this->cnonce = $this->_getCnonce(); - // XXX: in the future, when mandatory and/or optional extensions are defined in any updated RFC, - // this message can be updated. - $this->first_message_bare = 'n=' . $authcid . ',r=' . $this->cnonce; - return $this->gs2_header . $this->first_message_bare; - } - - /** - * Parses and verifies a non-empty SCRAM challenge. - * - * @param string $challenge The SCRAM challenge - * @return string|false The response to send; false in case of wrong challenge or if an initial response has not - * been generated first. - * @access private - */ - private function _generateResponse($challenge, $password) - { - // XXX: as I don't support mandatory extension, I would fail on them. - // And I simply ignore any optional extension. - $server_message_regexp = "#^r=([\x21-\x2B\x2D-\x7E]+),s=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?),i=([0-9]*)(,[A-Za-z]=[^,])*$#"; - if (!isset($this->cnonce, $this->gs2_header) - || !preg_match($server_message_regexp, $challenge, $matches)) - { - return false; - } - $nonce = $matches[1]; - $salt = base64_decode($matches[2]); - if (!$salt) - { - // Invalid Base64. - return false; - } - $i = intval($matches[3]); - - $cnonce = substr($nonce, 0, strlen($this->cnonce)); - if ($cnonce <> $this->cnonce) - { - // Invalid challenge! Are we under attack? - return false; - } - - $channel_binding = 'c=' . base64_encode($this->gs2_header); // TODO: support channel binding. - $final_message = $channel_binding . ',r=' . $nonce; // XXX: no extension. - - // TODO: $password = $this->normalize($password); // SASLprep profile of stringprep. - $saltedPassword = $this->hi($password, $salt, $i); - $this->saltedPassword = $saltedPassword; - $clientKey = call_user_func($this->hmac, $saltedPassword, "Client Key", TRUE); - $storedKey = call_user_func($this->hash, $clientKey, TRUE); - $authMessage = $this->first_message_bare . ',' . $challenge . ',' . $final_message; - $this->authMessage = $authMessage; - $clientSignature = call_user_func($this->hmac, $storedKey, $authMessage, TRUE); - $clientProof = $clientKey ^ $clientSignature; - $proof = ',p=' . base64_encode($clientProof); - - return $final_message . $proof; - } - - /** - * SCRAM has also a server verification step. On a successful outcome, it will send additional data which must - * absolutely be checked against this function. If this fails, the entity which we are communicating with is probably - * not the server as it has not access to your ServerKey. - * - * @param string $data The additional data sent along a successful outcome. - * @return bool Whether the server has been authenticated. - * If false, the client must close the connection and consider to be under a MITM attack. - * @access public - */ - public function processOutcome($data) - { - $verifier_regexp = '#^v=((?:[A-Za-z0-9/+]{4})*(?:[A-Za-z0-9]{3}=|[A-Xa-z0-9]{2}==)?)$#'; - if (!isset($this->saltedPassword, $this->authMessage) - || !preg_match($verifier_regexp, $data, $matches)) - { - // This cannot be an outcome, you never sent the challenge's response. - return false; - } - - $verifier = $matches[1]; - $proposed_serverSignature = base64_decode($verifier); - $serverKey = call_user_func($this->hmac, $this->saltedPassword, "Server Key", true); - $serverSignature = call_user_func($this->hmac, $serverKey, $this->authMessage, TRUE); - return ($proposed_serverSignature === $serverSignature); - } - - /** - * Hi() call, which is essentially PBKDF2 (RFC-2898) with HMAC-H() as the pseudorandom function. - * - * @param string $str The string to hash. - * @param string $hash The hash value. - * @param int $i The iteration count. - * @access private - */ - private function hi($str, $salt, $i) - { - $int1 = "\0\0\0\1"; - $ui = call_user_func($this->hmac, $str, $salt . $int1, true); - $result = $ui; - for ($k = 1; $k < $i; $k++) - { - $ui = call_user_func($this->hmac, $str, $ui, true); - $result = $result ^ $ui; - } - return $result; - } - - - /** - * Creates the client nonce for the response - * - * @return string The cnonce value - * @access private - * @author Richard Heyes - */ - private function _getCnonce() - { - // TODO: I reused the nonce function from the DigestMD5 class. - // I should probably make this a protected function in Common. - if (@file_exists('/dev/urandom') && $fd = @fopen('/dev/urandom', 'r')) { - return base64_encode(fread($fd, 32)); - - } elseif (@file_exists('/dev/random') && $fd = @fopen('/dev/random', 'r')) { - return base64_encode(fread($fd, 32)); - - } else { - $str = ''; - for ($i=0; $i<32; $i++) { - $str .= chr(mt_rand(0, 255)); - } - - return base64_encode($str); - } - } -} \ No newline at end of file diff --git a/sources/include/Mail.php b/sources/include/Mail.php deleted file mode 100644 index b1ba915..0000000 --- a/sources/include/Mail.php +++ /dev/null @@ -1,299 +0,0 @@ - - * @copyright 1997-2010 Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: Mail.php 307489 2011-01-14 19:06:57Z alec $ - * @link http://pear.php.net/package/Mail/ - */ - - - /** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail-1.2.0.tgz - * SVN trunk version r333509 - * - * - */ - - -/** - * PEAR's Mail:: interface. Defines the interface for implementing - * mailers under the PEAR hierarchy, and provides supporting functions - * useful in multiple mailer backends. - * - * @access public - * @version $Revision: 307489 $ - * @package Mail - */ -class Mail -{ - /** - * Line terminator used for separating header lines. - * @var string - */ - var $sep = "\r\n"; - - /** - * Provides an interface for generating Mail:: objects of various - * types - * - * @param string $driver The kind of Mail:: object to instantiate. - * @param array $params The parameters to pass to the Mail:: object. - * @return object Mail a instance of the driver class or if fails a PEAR Error - * @access public - */ - static function &factory($driver, $params = array()) - { - $driver = strtolower($driver); - $class = 'Mail_' . $driver; - if (class_exists($class)) { - $mailer = new $class($params); - return $mailer; - } else { - return Mail::raiseError('Unable to find class for driver ' . $driver); - } - } - - /** - * Implements Mail::send() function using php's built-in mail() - * command. - * - * @param mixed $recipients Either a comma-seperated list of recipients - * (RFC822 compliant), or an array of recipients, - * each RFC822 valid. This may contain recipients not - * specified in the headers, for Bcc:, resending - * messages, etc. - * - * @param array $headers The array of headers to send with the mail, in an - * associative array, where the array key is the - * header name (ie, 'Subject'), and the array value - * is the header value (ie, 'test'). The header - * produced from those values would be 'Subject: - * test'. - * - * @param string $body The full text of the message body, including any - * Mime parts, etc. - * - * @return mixed Returns true on success, or a PEAR_Error - * containing a descriptive error message on - * failure. - * - * @access public - * @deprecated use Mail_mail::send instead - */ - function send($recipients, $headers, $body) - { - if (!is_array($headers)) { - return Mail::raiseError('$headers must be an array'); - } - - $result = $this->_sanitizeHeaders($headers); - //if (is_a($result, 'PEAR_Error')) { - if ($result === false) { - return $result; - } - - // if we're passed an array of recipients, implode it. - if (is_array($recipients)) { - $recipients = implode(', ', $recipients); - } - - // get the Subject out of the headers array so that we can - // pass it as a seperate argument to mail(). - $subject = ''; - if (isset($headers['Subject'])) { - $subject = $headers['Subject']; - unset($headers['Subject']); - } - - // flatten the headers out. - list(, $text_headers) = Mail::prepareHeaders($headers); - - return mail($recipients, $subject, $body, $text_headers); - } - - /** - * Sanitize an array of mail headers by removing any additional header - * strings present in a legitimate header's value. The goal of this - * filter is to prevent mail injection attacks. - * - * @param array $headers The associative array of headers to sanitize. - * - * @access private - */ - function _sanitizeHeaders(&$headers) - { - foreach ($headers as $key => $value) { - $headers[$key] = - preg_replace('=((||0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', - null, $value); - } - - return true; - } - - /** - * Take an array of mail headers and return a string containing - * text usable in sending a message. - * - * @param array $headers The array of headers to prepare, in an associative - * array, where the array key is the header name (ie, - * 'Subject'), and the array value is the header - * value (ie, 'test'). The header produced from those - * values would be 'Subject: test'. - * - * @return mixed Returns false if it encounters a bad address, - * otherwise returns an array containing two - * elements: Any From: address found in the headers, - * and the plain text version of the headers. - * @access private - */ - function prepareHeaders($headers) - { - $lines = array(); - $from = null; - - foreach ($headers as $key => $value) { - if (strcasecmp($key, 'From') === 0) { - $parser = new Mail_RFC822(); - $addresses = $parser->parseAddressList($value, 'localhost', false); - //if (is_a($addresses, 'PEAR_Error')) { - if ($addresses === false) { - return $addresses; - } - - $from = $addresses[0]->mailbox . '@' . $addresses[0]->host; - - // Reject envelope From: addresses with spaces. - if (strstr($from, ' ')) { - return false; - } - - $lines[] = $key . ': ' . $value; - } elseif (strcasecmp($key, 'Received') === 0) { - $received = array(); - if (is_array($value)) { - foreach ($value as $line) { - $received[] = $key . ': ' . $line; - } - } - else { - $received[] = $key . ': ' . $value; - } - // Put Received: headers at the top. Spam detectors often - // flag messages with Received: headers after the Subject: - // as spam. - $lines = array_merge($received, $lines); - } else { - // If $value is an array (i.e., a list of addresses), convert - // it to a comma-delimited string of its elements (addresses). - if (is_array($value)) { - $value = implode(', ', $value); - } - $lines[] = $key . ': ' . $value; - } - } - - return array($from, join($this->sep, $lines)); - } - - /** - * Take a set of recipients and parse them, returning an array of - * bare addresses (forward paths) that can be passed to sendmail - * or an smtp server with the rcpt to: command. - * - * @param mixed Either a comma-seperated list of recipients - * (RFC822 compliant), or an array of recipients, - * each RFC822 valid. - * - * @return mixed An array of forward paths (bare addresses) or a PEAR_Error - * object if the address list could not be parsed. - * @access private - */ - function parseRecipients($recipients) - { - // if we're passed an array, assume addresses are valid and - // implode them before parsing. - if (is_array($recipients)) { - $recipients = implode(', ', $recipients); - } - - // Parse recipients, leaving out all personal info. This is - // for smtp recipients, etc. All relevant personal information - // should already be in the headers. - $parser = new Mail_RFC822(); - $addresses = $parser->parseAddressList($recipients, 'localhost', false); - - // If parseAddressList() returned a PEAR_Error object, just return it. - //if (is_a($addresses, 'PEAR_Error')) { - if ($addresses === false) { - return $addresses; - } - - $recipients = array(); - if (is_array($addresses)) { - foreach ($addresses as $ob) { - $recipients[] = $ob->mailbox . '@' . $ob->host; - } - } - - // Remove duplicated - $recipients = array_unique($recipients); - - return $recipients; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Mail error: ". $message); - return false; - } -} diff --git a/sources/include/Mail/mail.php b/sources/include/Mail/mail.php deleted file mode 100644 index 1d56eec..0000000 --- a/sources/include/Mail/mail.php +++ /dev/null @@ -1,193 +0,0 @@ - - * @copyright 2010 Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: mail.php 294747 2010-02-08 08:18:33Z clockwerx $ - * @link http://pear.php.net/package/Mail/ - */ - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail-1.2.0.tgz - * - * - */ - -/** - * internal PHP-mail() implementation of the PEAR Mail:: interface. - * @package Mail - * @version $Revision: 294747 $ - */ -class Mail_mail extends Mail { - - /** - * Any arguments to pass to the mail() function. - * @var string - */ - var $_params = ''; - - /** - * Constructor. - * - * Instantiates a new Mail_mail:: object based on the parameters - * passed in. - * - * @param array $params Extra arguments for the mail() function. - */ - function Mail_mail($params = null) - { - // The other mail implementations accept parameters as arrays. - // In the interest of being consistent, explode an array into - // a string of parameter arguments. - if (is_array($params)) { - $this->_params = join(' ', $params); - } else { - $this->_params = $params; - } - - /* Because the mail() function may pass headers as command - * line arguments, we can't guarantee the use of the standard - * "\r\n" separator. Instead, we use the system's native line - * separator. */ - if (defined('PHP_EOL')) { - $this->sep = PHP_EOL; - } else { - $this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n"; - } - } - - /** - * Implements Mail_mail::send() function using php's built-in mail() - * command. - * - * @param mixed $recipients Either a comma-seperated list of recipients - * (RFC822 compliant), or an array of recipients, - * each RFC822 valid. This may contain recipients not - * specified in the headers, for Bcc:, resending - * messages, etc. - * - * @param array $headers The array of headers to send with the mail, in an - * associative array, where the array key is the - * header name (ie, 'Subject'), and the array value - * is the header value (ie, 'test'). The header - * produced from those values would be 'Subject: - * test'. - * - * @param string $body The full text of the message body, including any - * Mime parts, etc. - * - * @return mixed Returns true on success, or a PEAR_Error - * containing a descriptive error message on - * failure. - * - * @access public - */ - function send($recipients, $headers, $body) - { - if (!is_array($headers)) { - return Mail_mail::raiseError('$headers must be an array'); - } - - $result = $this->_sanitizeHeaders($headers); - //if (is_a($result, 'PEAR_Error')) { - if ($result === false) { - return $result; - } - - // If we're passed an array of recipients, implode it. - if (is_array($recipients)) { - $recipients = implode(', ', $recipients); - } - - // Get the Subject out of the headers array so that we can - // pass it as a seperate argument to mail(). - $subject = ''; - if (isset($headers['Subject'])) { - $subject = $headers['Subject']; - unset($headers['Subject']); - } - - // Also remove the To: header. The mail() function will add its own - // To: header based on the contents of $recipients. - unset($headers['To']); - - // Flatten the headers out. - $headerElements = $this->prepareHeaders($headers); - //if (is_a($headerElements, 'PEAR_Error')) { - if ($headerElements === false) { - return $headerElements; - } - list(, $text_headers) = $headerElements; - - // We only use mail()'s optional fifth parameter if the additional - // parameters have been provided and we're not running in safe mode. - if (empty($this->_params) || ini_get('safe_mode')) { - $result = mail($recipients, $subject, $body, $text_headers); - } else { - $result = mail($recipients, $subject, $body, $text_headers, - $this->_params); - } - - // If the mail() function returned failure, we need to create a - // PEAR_Error object and return it instead of the boolean result. - if ($result === false) { - $result = Mail_mail::raiseError('mail() returned failure'); - } - - return $result; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Mail error: ". $message); - return false; - } -} diff --git a/sources/include/Mail/sendmail.php b/sources/include/Mail/sendmail.php deleted file mode 100644 index 984c009..0000000 --- a/sources/include/Mail/sendmail.php +++ /dev/null @@ -1,197 +0,0 @@ - | -// +----------------------------------------------------------------------+ - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail-1.2.0.tgz - * - * - */ - -/** - * Sendmail implementation of the PEAR Mail:: interface. - * @access public - * @package Mail - * @version $Revision: 294744 $ - */ -class Mail_sendmail extends Mail { - - /** - * The location of the sendmail or sendmail wrapper binary on the - * filesystem. - * @var string - */ - var $sendmail_path = '/usr/sbin/sendmail'; - - /** - * Any extra command-line parameters to pass to the sendmail or - * sendmail wrapper binary. - * @var string - */ - var $sendmail_args = '-i'; - - /** - * Constructor. - * - * Instantiates a new Mail_sendmail:: object based on the parameters - * passed in. It looks for the following parameters: - * sendmail_path The location of the sendmail binary on the - * filesystem. Defaults to '/usr/sbin/sendmail'. - * - * sendmail_args Any extra parameters to pass to the sendmail - * or sendmail wrapper binary. - * - * If a parameter is present in the $params array, it replaces the - * default. - * - * @param array $params Hash containing any parameters different from the - * defaults. - * @access public - */ - function Mail_sendmail($params) - { - if (isset($params['sendmail_path'])) { - $this->sendmail_path = $params['sendmail_path']; - } - if (isset($params['sendmail_args'])) { - $this->sendmail_args = $params['sendmail_args']; - } - - /* - * Because we need to pass message headers to the sendmail program on - * the commandline, we can't guarantee the use of the standard "\r\n" - * separator. Instead, we use the system's native line separator. - */ - if (defined('PHP_EOL')) { - $this->sep = PHP_EOL; - } else { - $this->sep = (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n"; - } - } - - /** - * Implements Mail::send() function using the sendmail - * command-line binary. - * - * @param mixed $recipients Either a comma-seperated list of recipients - * (RFC822 compliant), or an array of recipients, - * each RFC822 valid. This may contain recipients not - * specified in the headers, for Bcc:, resending - * messages, etc. - * - * @param array $headers The array of headers to send with the mail, in an - * associative array, where the array key is the - * header name (ie, 'Subject'), and the array value - * is the header value (ie, 'test'). The header - * produced from those values would be 'Subject: - * test'. - * - * @param string $body The full text of the message body, including any - * Mime parts, etc. - * - * @return mixed Returns true on success, or a PEAR_Error - * containing a descriptive error message on - * failure. - * @access public - */ - function send($recipients, $headers, $body) - { - if (!is_array($headers)) { - return Mail_sendmail::raiseError('$headers must be an array'); - } - - $result = $this->_sanitizeHeaders($headers); - //if (is_a($result, 'PEAR_Error')) { - if ($result === false) { - return $result; - } - - $recipients = $this->parseRecipients($recipients); - //if (is_a($recipients, 'PEAR_Error')) { - if ($recipients === false) { - return $recipients; - } - $recipients = implode(' ', array_map('escapeshellarg', $recipients)); - - $headerElements = $this->prepareHeaders($headers); - //if (is_a($headerElements, 'PEAR_Error')) { - if ($headerElements === false) { - return $headerElements; - } - list($from, $text_headers) = $headerElements; - - /* Since few MTAs are going to allow this header to be forged - * unless it's in the MAIL FROM: exchange, we'll use - * Return-Path instead of From: if it's set. */ - if (!empty($headers['Return-Path'])) { - $from = $headers['Return-Path']; - } - - if (!isset($from)) { - return Mail_sendmail::raiseError('No from address given.'); - } elseif (strpos($from, ' ') !== false || - strpos($from, ';') !== false || - strpos($from, '&') !== false || - strpos($from, '`') !== false) { - return Mail_sendmail::raiseError('From address specified with dangerous characters.'); - } - - $from = escapeshellarg($from); // Security bug #16200 - - $mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w'); - if (!$mail) { - return Mail_sendmail::raiseError('Failed to open sendmail [' . $this->sendmail_path . '] for execution.'); - } - - // Write the headers following by two newlines: one to end the headers - // section and a second to separate the headers block from the body. - fputs($mail, $text_headers . $this->sep . $this->sep); - - fputs($mail, $body); - $result = pclose($mail); - if (version_compare(phpversion(), '4.2.3') == -1) { - // With older php versions, we need to shift the pclose - // result to get the exit code. - $result = $result >> 8 & 0xFF; - } - - if ($result != 0) { - return Mail_sendmail::raiseError('sendmail returned error code ' . $result, - $result); - } - - return true; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Mail error: ". $message); - return false; - } -} diff --git a/sources/include/Mail/smtp.php b/sources/include/Mail/smtp.php deleted file mode 100644 index 9318f5d..0000000 --- a/sources/include/Mail/smtp.php +++ /dev/null @@ -1,500 +0,0 @@ - - * @author Chuck Hagenbuch - * @copyright 2010 Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php New BSD License - * @version CVS: $Id: smtp.php 307488 2011-01-14 19:00:54Z alec $ - * @link http://pear.php.net/package/Mail/ - */ - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail-1.2.0.tgz - * - * - */ - -/** Error: Failed to create a Net_SMTP object */ -define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000); - -/** Error: Failed to connect to SMTP server */ -define('PEAR_MAIL_SMTP_ERROR_CONNECT', 10001); - -/** Error: SMTP authentication failure */ -define('PEAR_MAIL_SMTP_ERROR_AUTH', 10002); - -/** Error: No From: address has been provided */ -define('PEAR_MAIL_SMTP_ERROR_FROM', 10003); - -/** Error: Failed to set sender */ -define('PEAR_MAIL_SMTP_ERROR_SENDER', 10004); - -/** Error: Failed to add recipient */ -define('PEAR_MAIL_SMTP_ERROR_RECIPIENT', 10005); - -/** Error: Failed to send data */ -define('PEAR_MAIL_SMTP_ERROR_DATA', 10006); - -/** - * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class. - * @access public - * @package Mail - * @version $Revision: 307488 $ - */ -class Mail_smtp extends Mail { - - /** - * SMTP connection object. - * - * @var object - * @access private - */ - var $_smtp = null; - - /** - * The list of service extension parameters to pass to the Net_SMTP - * mailFrom() command. - * @var array - */ - var $_extparams = array(); - - /** - * The SMTP host to connect to. - * @var string - */ - var $host = 'localhost'; - - /** - * The port the SMTP server is on. - * @var integer - */ - var $port = 25; - - /** - * Should SMTP authentication be used? - * - * This value may be set to true, false or the name of a specific - * authentication method. - * - * If the value is set to true, the Net_SMTP package will attempt to use - * the best authentication method advertised by the remote SMTP server. - * - * @var mixed - */ - var $auth = false; - - /** - * The username to use if the SMTP server requires authentication. - * @var string - */ - var $username = ''; - - /** - * The password to use if the SMTP server requires authentication. - * @var string - */ - var $password = ''; - - /** - * Hostname or domain that will be sent to the remote SMTP server in the - * HELO / EHLO message. - * - * @var string - */ - var $localhost = 'localhost'; - - /** - * SMTP connection timeout value. NULL indicates no timeout. - * - * @var integer - */ - var $timeout = null; - - /** - * Turn on Net_SMTP debugging? - * - * @var boolean $debug - */ - var $debug = false; - - /** - * Indicates whether or not the SMTP connection should persist over - * multiple calls to the send() method. - * - * @var boolean - */ - var $persist = false; - - /** - * Use SMTP command pipelining (specified in RFC 2920) if the SMTP server - * supports it. This speeds up delivery over high-latency connections. By - * default, use the default value supplied by Net_SMTP. - * @var bool - */ - var $pipelining; - - /** - * Require verification of SSL certificate used. - * @var bool - */ - var $verify_peer = true; - - /** - * Require verification of peer name - * @var bool - */ - var $verify_peer_name = true; - - /** - * Allow self-signed certificates. Requires verify_peer - * @var bool - */ - var $allow_self_signed = false; - - /** - * Constructor. - * - * Instantiates a new Mail_smtp:: object based on the parameters - * passed in. It looks for the following parameters: - * host The server to connect to. Defaults to localhost. - * port The port to connect to. Defaults to 25. - * auth SMTP authentication. Defaults to none. - * username The username to use for SMTP auth. No default. - * password The password to use for SMTP auth. No default. - * localhost The local hostname / domain. Defaults to localhost. - * timeout The SMTP connection timeout. Defaults to none. - * verp Whether to use VERP or not. Defaults to false. - * DEPRECATED as of 1.2.0 (use setMailParams()). - * debug Activate SMTP debug mode? Defaults to false. - * persist Should the SMTP connection persist? - * pipelining Use SMTP command pipelining - * - * If a parameter is present in the $params array, it replaces the - * default. - * - * @param array Hash containing any parameters different from the - * defaults. - * @access public - */ - function Mail_smtp($params) - { - if (isset($params['host'])) $this->host = $params['host']; - if (isset($params['port'])) $this->port = $params['port']; - if (isset($params['auth'])) $this->auth = $params['auth']; - if (isset($params['username'])) $this->username = $params['username']; - if (isset($params['password'])) $this->password = $params['password']; - if (isset($params['localhost'])) $this->localhost = $params['localhost']; - if (isset($params['timeout'])) $this->timeout = $params['timeout']; - if (isset($params['debug'])) $this->debug = (bool)$params['debug']; - if (isset($params['persist'])) $this->persist = (bool)$params['persist']; - if (isset($params['pipelining'])) $this->pipelining = (bool)$params['pipelining']; - if (isset($params['verify_peer'])) $this->verify_peer = (bool)$params['verify_peer']; - if (isset($params['verify_peer_name'])) $this->verify_peer_name = (bool)$params['verify_peer_name']; - if (isset($params['allow_self_signed'])) $this->allow_self_signed = (bool)$params['allow_self_signed']; - - // Deprecated options - if (isset($params['verp'])) { - $this->addServiceExtensionParameter('XVERP', is_bool($params['verp']) ? null : $params['verp']); - } - - register_shutdown_function(array(&$this, '_Mail_smtp')); - } - - /** - * Destructor implementation to ensure that we disconnect from any - * potentially-alive persistent SMTP connections. - */ - function _Mail_smtp() - { - $this->disconnect(); - } - - /** - * Implements Mail::send() function using SMTP. - * - * @param mixed $recipients Either a comma-seperated list of recipients - * (RFC822 compliant), or an array of recipients, - * each RFC822 valid. This may contain recipients not - * specified in the headers, for Bcc:, resending - * messages, etc. - * - * @param array $headers The array of headers to send with the mail, in an - * associative array, where the array key is the - * header name (e.g., 'Subject'), and the array value - * is the header value (e.g., 'test'). The header - * produced from those values would be 'Subject: - * test'. - * - * @param string $body The full text of the message body, including any - * MIME parts, etc. - * - * @return mixed Returns true on success, or a PEAR_Error - * containing a descriptive error message on - * failure. - * @access public - */ - function send($recipients, $headers, $body) - { - /* If we don't already have an SMTP object, create one. */ - $result = &$this->getSMTPObject(); - //if (PEAR::isError($result)) { - if ($result === false) { - return $result; - } - - if (!is_array($headers)) { - return Mail_smtp::raiseError('$headers must be an array'); - } - - $this->_sanitizeHeaders($headers); - - $headerElements = $this->prepareHeaders($headers); - //if (is_a($headerElements, 'PEAR_Error')) { - if ($headerElements === false) { - $this->_smtp->rset(); - return $headerElements; - } - list($from, $textHeaders) = $headerElements; - - /* Since few MTAs are going to allow this header to be forged - * unless it's in the MAIL FROM: exchange, we'll use - * Return-Path instead of From: if it's set. */ - if (!empty($headers['Return-Path'])) { - $from = $headers['Return-Path']; - } - - if (!isset($from)) { - $this->_smtp->rset(); - return Mail_smtp::raiseError('No From: address has been provided', - PEAR_MAIL_SMTP_ERROR_FROM); - } - - $params = null; - if (!empty($this->_extparams)) { - foreach ($this->_extparams as $key => $val) { - $params .= ' ' . $key . (is_null($val) ? '' : '=' . $val); - } - } - //if (PEAR::isError($res = $this->_smtp->mailFrom($from, ltrim($params)))) { - if (($res = $this->_smtp->mailFrom($from, ltrim($params))) === false) { - $error = $this->_error("Failed to set sender: $from", $res); - $this->_smtp->rset(); - return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_SENDER); - } - - $recipients = $this->parseRecipients($recipients); - //if (is_a($recipients, 'PEAR_Error')) { - if ($recipients === false) { - $this->_smtp->rset(); - return $recipients; - } - - foreach ($recipients as $recipient) { - $res = $this->_smtp->rcptTo($recipient); - //if (is_a($res, 'PEAR_Error')) { - if ($res === false) { - $error = $this->_error("Failed to add recipient: $recipient", $res); - $this->_smtp->rset(); - return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_RECIPIENT); - } - } - - /* Send the message's headers and the body as SMTP data. */ - $res = $this->_smtp->data($body, $textHeaders); - list(,$args) = $this->_smtp->getResponse(); - - if (preg_match("/Ok: queued as (.*)/", $args, $queued)) { - $this->queued_as = $queued[1]; - } - - /* we need the greeting; from it we can extract the authorative name of the mail server we've really connected to. - * ideal if we're connecting to a round-robin of relay servers and need to track which exact one took the email */ - $this->greeting = $this->_smtp->getGreeting(); - - //if (is_a($res, 'PEAR_Error')) { - if ($res === false) { - $error = $this->_error('Failed to send data', $res); - $this->_smtp->rset(); - return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_DATA); - } - - /* If persistent connections are disabled, destroy our SMTP object. */ - if ($this->persist === false) { - $this->disconnect(); - } - - return true; - } - - /** - * Connect to the SMTP server by instantiating a Net_SMTP object. - * - * @return mixed Returns a reference to the Net_SMTP object on success, or - * a PEAR_Error containing a descriptive error message on - * failure. - * - * @since 1.2.0 - * @access public - */ - function &getSMTPObject() - { - if (is_object($this->_smtp) !== false) { - return $this->_smtp; - } - - $this->_smtp = &new Net_SMTP($this->host, - $this->port, - $this->localhost, - $this->pipelining, - 0, //timeout - null, //socket_options - $this->verify_peer, - $this->verify_peer_name, - $this->allow_self_signed); - - /* If we still don't have an SMTP object at this point, fail. */ - if (is_object($this->_smtp) === false) { - return Mail_smtp::raiseError('Failed to create a Net_SMTP object', - PEAR_MAIL_SMTP_ERROR_CREATE); - } - - /* Configure the SMTP connection. */ - if ($this->debug) { - $this->_smtp->setDebug(true); - } - - /* Attempt to connect to the configured SMTP server. */ - //if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) { - if (($res = $this->_smtp->connect($this->timeout)) === false) { - $error = $this->_error('Failed to connect to ' . - $this->host . ':' . $this->port, - $res); - return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT); - } - - /* Attempt to authenticate if authentication has been enabled. */ - if ($this->auth) { - $method = is_string($this->auth) ? $this->auth : ''; - - //if (PEAR::isError($res = $this->_smtp->auth($this->username, $this->password, $method))) { - if (($res = $this->_smtp->auth($this->username, $this->password, $method)) === false) { - $error = $this->_error("$method authentication failure", - $res); - $this->_smtp->rset(); - return Mail_smtp::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH); - } - } - - return $this->_smtp; - } - - /** - * Add parameter associated with a SMTP service extension. - * - * @param string Extension keyword. - * @param string Any value the keyword needs. - * - * @since 1.2.0 - * @access public - */ - function addServiceExtensionParameter($keyword, $value = null) - { - $this->_extparams[$keyword] = $value; - } - - /** - * Disconnect and destroy the current SMTP connection. - * - * @return boolean True if the SMTP connection no longer exists. - * - * @since 1.1.9 - * @access public - */ - function disconnect() - { - /* If we have an SMTP object, disconnect and destroy it. */ - if (is_object($this->_smtp) && $this->_smtp->disconnect()) { - $this->_smtp = null; - } - - /* We are disconnected if we no longer have an SMTP object. */ - return ($this->_smtp === null); - } - - /** - * Build a standardized string describing the current SMTP error. - * - * @param string $text Custom string describing the error context. - * @param object $error Reference to the current PEAR_Error object. - * - * @return string A string describing the current SMTP error. - * - * @since 1.1.7 - * @access private - */ - function _error($text, &$error) - { - /* Split the SMTP response into a code and a response string. */ - list($code, $response) = $this->_smtp->getResponse(); - - /* Build our standardized error string. */ - return $text -// . ' [SMTP: ' . $error->getMessage() - . ' [SMTP: ' - . " (code: $code, response: $response)]"; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Mail error: ". $message); - return false; - } -} diff --git a/sources/include/Net/SMTP.php b/sources/include/Net/SMTP.php deleted file mode 100644 index 7b15281..0000000 --- a/sources/include/Net/SMTP.php +++ /dev/null @@ -1,1303 +0,0 @@ - | -// | Jon Parise | -// | Damian Alejandro Fernandez Sosa | -// +----------------------------------------------------------------------+ - - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Net_SMTP-1.6.2.tgz - * https://github.com/pear/Net_SMTP Commit 558b92f5c2ecbb857094a3926a100e51211a08c2 2014/03/09 - * - * - */ - -//require_once 'PEAR.php'; -//require_once 'PEAR/Exception.php'; - -/** - * Provides an implementation of the SMTP protocol using PEAR's - * Net_Socket:: class. - * - * @package Net_SMTP - * @author Chuck Hagenbuch - * @author Jon Parise - * @author Damian Alejandro Fernandez Sosa - * - * @example basic.php A basic implementation of the Net_SMTP package. - */ -class Net_SMTP -{ - /** - * The server to connect to. - * - * @var string - */ - public $host = 'localhost'; - - /** - * The port to connect to. - * - * @var int - */ - public $port = 25; - - /** - * The value to give when sending EHLO or HELO. - * - * @var string - */ - public $localhost = 'localhost'; - - /** - * List of supported authentication methods, in preferential order. - * - * @var array - */ - public $auth_methods = array(); - - /** - * Use SMTP command pipelining (specified in RFC 2920) if the SMTP - * server supports it. - * - * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(), - * somlFrom() and samlFrom() do not wait for a response from the - * SMTP server but return immediately. - * - * @var bool - */ - public $pipelining = false; - - /** - * Number of pipelined commands. - * - * @var int - */ - protected $_pipelined_commands = 0; - - /** - * Should debugging output be enabled? - * - * @var boolean - */ - protected $_debug = false; - - /** - * Debug output handler. - * - * @var callback - */ - protected $_debug_handler = null; - - /** - * The socket resource being used to connect to the SMTP server. - * - * @var resource - */ - protected $_socket = null; - - /** - * Array of socket options that will be passed to Net_Socket::connect(). - * - * @see stream_context_create() - * - * @var array - */ - protected $_socket_options = null; - - /** - * The socket I/O timeout value in seconds. - * - * @var int - */ - protected $_timeout = 0; - - /** - * The most recent server response code. - * - * @var int - */ - protected $_code = -1; - - /** - * The most recent server response arguments. - * - * @var array - */ - protected $_arguments = array(); - - /** - * Stores the SMTP server's greeting string. - * - * @var string - */ - protected $_greeting = null; - - /** - * Stores detected features of the SMTP server. - * - * @var array - */ - protected $_esmtp = array(); - - /** - * Require verification of SSL certificate used. - * - * @var bool - */ - protected $_verify_peer; - - /** - * Require verification of peer name - * - * @var bool - */ - protected $_verify_peer_name; - - /** - * Allow self-signed certificates. Requires verify_peer - * - * @var bool - */ - protected $_allow_self_signed; - - /** - * Instantiates a new Net_SMTP object, overriding any defaults - * with parameters that are passed in. - * - * If you have SSL support in PHP, you can connect to a server - * over SSL using an 'ssl://' prefix: - * - * // 465 is a common smtps port. - * $smtp = new Net_SMTP('ssl://mail.host.com', 465); - * $smtp->connect(); - * - * @param string $host The server to connect to. - * @param integer $port The port to connect to. - * @param string $localhost The value to give when sending EHLO or HELO. - * @param boolean $pipeling Use SMTP command pipelining - * @param integer $timeout Socket I/O timeout in seconds. - * @param array $socket_options Socket stream_context_create() options. - * @param boolean $verify_peer Require verification of SSL certificate used - * @param boolean $verify_peer_name Require verification of peer name - * @param boolean $allow_self_signed Allow self-signed certificates. Requires verify_peer - */ - public function __construct($host = null, $port = null, $localhost = null, - $pipelining = false, $timeout = 0, - $socket_options = null, - $verify_peer = true, $verify_peer_name = true, $allow_self_signed = false) - { - if (isset($host)) { - $this->host = $host; - } - if (isset($port)) { - $this->port = $port; - } - if (isset($localhost)) { - $this->localhost = $localhost; - } - $this->pipelining = $pipelining; - - $this->_socket = new Net_Socket(); - $this->_socket_options = $socket_options; - - // SSL connection, we need to modify the socket_options - if (strpos($this->host, "ssl://") === 0) { - if ($this->_socket_options == null) - $this->_socket_options = array(); - - if (!array_key_exists('ssl', $this->_socket_options)) - $this->_socket_options['ssl'] = array(); - - $this->_socket_options['ssl']['verify_peer'] = $verify_peer; - $this->_socket_options['ssl']['allow_self_signed'] = $allow_self_signed; - // This option was introduced in 5.6 - if (version_compare(phpversion(), "5.6.0", ">=")) - $this->_socket_options['ssl']['verify_peer_name'] = $verify_peer_name; - } - - $this->_timeout = $timeout; - - // We also need this for use in the STARTTLS command - $this->_verify_peer = $verify_peer; - $this->_verify_peer_name = $verify_peer_name; - $this->_allow_self_signed = $allow_self_signed; - - /* Include the Auth_SASL package. If the package is available, we - * enable the authentication methods that depend upon it. */ - $this->setAuthMethod('CRAM-MD5', array($this, '_authCram_MD5')); - $this->setAuthMethod('DIGEST-MD5', array($this, '_authDigest_MD5')); - - /* These standard authentication methods are always available. */ - $this->setAuthMethod('LOGIN', array($this, '_authLogin'), false); - $this->setAuthMethod('PLAIN', array($this, '_authPlain'), false); - } - - /** - * Set the socket I/O timeout value in seconds plus microseconds. - * - * @param integer $seconds Timeout value in seconds. - * @param integer $microseconds Additional value in microseconds. - */ - public function setTimeout($seconds, $microseconds = 0) - { - return $this->_socket->setTimeout($seconds, $microseconds); - } - - /** - * Set the value of the debugging flag. - * - * @param boolean $debug New value for the debugging flag. - */ - public function setDebug($debug, $handler = null) - { - $this->_debug = $debug; - $this->_debug_handler = $handler; - } - - /** - * Write the given debug text to the current debug output handler. - * - * @param string $message Debug message text. - */ - protected function _debug($message) - { - if ($this->_debug) { - if ($this->_debug_handler) { - call_user_func_array($this->_debug_handler, - array(&$this, $message)); - } else { - ZLog::Write(LOGLEVEL_DEBUG, "Net_SMTP DEBUG: ". $message); - } - } - } - - /** - * Send the given string of data to the server. - * - * @param string $data The string of data to send. - * - * @return integer The number of bytes that were actually written. - * @throws PEAR_Exception - */ - protected function _send($data) - { - $this->_debug("Send: $data"); - - $result = $this->_socket->write($data); - if ($result === false) { -// return Net_SMTP::raiseError('Failed to write to socket: ' . $result->getMessage(), -// $result); - return Net_SMTP::raiseError('Failed to write to socket: '); - } - - return $result; - } - - /** - * Send a command to the server with an optional string of - * arguments. A carriage return / linefeed (CRLF) sequence will - * be appended to each command string before it is sent to the - * SMTP server - an error will be thrown if the command string - * already contains any newline characters. Use _send() for - * commands that must contain newlines. - * - * @param string $command The SMTP command to send to the server. - * @param string $args A string of optional arguments to append - * to the command. - * - * @return integer The number of bytes that were actually written. - * @throws PEAR_Exception - */ - protected function _put($command, $args = '') - { - if (!empty($args)) { - $command .= ' ' . $args; - } - - if (strcspn($command, "\r\n") !== strlen($command)) { - return Net_SMTP::raiseError('Commands cannot contain newlines'); - } - - return $this->_send($command . "\r\n"); - } - - /** - * Read a reply from the SMTP server. The reply consists of a response - * code and a response message. - * - * @see getResponse - * - * @param mixed $valid The set of valid response codes. These - * may be specified as an array of integer - * values or as a single integer value. - * @param bool $later Do not parse the response now, but wait - * until the last command in the pipelined - * command group - * - * @throws PEAR_Exception - */ - protected function _parseResponse($valid, $later = false) - { - $this->_code = -1; - $this->_arguments = array(); - - if ($later) { - ++$this->_pipelined_commands; - return; - } - - for ($i = 0; $i <= $this->_pipelined_commands; ++$i) { - while ($line = $this->_socket->readLine()) { - $this->_debug("Recv: $line"); - - /* If we receive an empty line, the connection was closed. */ - if (empty($line)) { - $this->disconnect(); - return Net_SMTP::raiseError('Connection was closed', -// null, PEAR_ERROR_RETURN); - null, 1); - } - - /* Read the code and store the rest in the arguments array. */ - $code = substr($line, 0, 3); - $this->_arguments[] = trim(substr($line, 4)); - - /* Check the syntax of the response code. */ - if (is_numeric($code)) { - $this->_code = (int)$code; - } else { - $this->_code = -1; - break; - } - - /* If this is not a multiline response, we're done. */ - if (substr($line, 3, 1) != '-') { - break; - } - } - } - - $this->_pipelined_commands = 0; - - /* Compare the server's response code with the valid code/codes. */ - if ((is_int($valid) && ($this->_code === $valid)) || - (is_array($valid) && in_array($this->_code, $valid, true))) { - return; - } - - return Net_SMTP::raiseError('Invalid response code received from server', -// $this->_code, PEAR_ERROR_RETURN); - $this->_code, 1); - } - - /** - * Issue an SMTP command and verify its response. - * - * @param string $command The SMTP command string or data. - * @param mixed $valid The set of valid response codes. These - * may be specified as an array of integer - * values or as a single integer value. - * - * @throws PEAR_Exception - */ - public function command($command, $valid) - { - //if (PEAR::isError($error = $this->_put($command))) { - if (($error = $this->_put($command)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse($valid))) { - if (($error = $this->_parseResponse($valid)) === false) { - return $error; - } - - return true; - } - - /** - * Return a 2-tuple containing the last response from the SMTP server. - * - * @return array A two-element array: the first element contains the - * response code as an integer and the second element - * contains the response's arguments as a string. - */ - public function getResponse() - { - return array($this->_code, join("\n", $this->_arguments)); - } - - /** - * Return the SMTP server's greeting string. - * - * @return string A string containing the greeting string, or null if a - * greeting has not been received. - */ - public function getGreeting() - { - return $this->_greeting; - } - - /** - * Attempt to connect to the SMTP server. - * - * @param int $timeout The timeout value (in seconds) for the - * socket connection attempt. - * @param bool $persistent Should a persistent socket connection - * be used? - * - * @throws PEAR_Exception - */ - public function connect($timeout = null, $persistent = false) - { - $this->_greeting = null; - $result = $this->_socket->connect($this->host, $this->port, - $persistent, $timeout, - $this->_socket_options); - //if (PEAR::isError($result)) { - if ($result === false) { -// return Net_SMTP::raiseError('Failed to connect socket: ' . -// $result->getMessage()); - return Net_SMTP::raiseError('Failed to connect socket: '); - } - - /* - * Now that we're connected, reset the socket's timeout value for - * future I/O operations. This allows us to have different socket - * timeout values for the initial connection (our $timeout parameter) - * and all other socket operations. - */ - if ($this->_timeout > 0) { - //if (PEAR::isError($error = $this->setTimeout($this->_timeout))) { - if (($error = $this->setTimeout($this->_timeout)) === false) { - return $error; - } - } - - //if (PEAR::isError($error = $this->_parseResponse(220))) { - if (($error = $this->_parseResponse(220)) === false) { - return $error; - } - - /* Extract and store a copy of the server's greeting string. */ - list(, $this->_greeting) = $this->getResponse(); - - //if (PEAR::isError($error = $this->_negotiate())) { - if (($error = $this->_negotiate()) === false) { - return $error; - } - - return true; - } - - /** - * Attempt to disconnect from the SMTP server. - * - * @throws PEAR_Exception - */ - public function disconnect() - { - //if (PEAR::isError($error = $this->_put('QUIT'))) { - if (($error = $this->_put('QUIT')) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(221))) { - if (($error = $this->_parseResponse(221)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_socket->disconnect())) { - if (($error = $this->_socket->disconnect()) === false) { - return Net_SMTP::raiseError('Failed to disconnect socket: ' . - $error->getMessage()); - } - - return true; - } - - /** - * Attempt to send the EHLO command and obtain a list of ESMTP - * extensions available, and failing that just send HELO. - * - * @throws PEAR_Exception - */ - protected function _negotiate() - { - //if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) { - if (($error = $this->_put('EHLO', $this->localhost)) === false) { - return $error; - } - - //if (PEAR::isError($this->_parseResponse(250))) { - if (($this->_parseResponse(250)) === false) { - /* If the EHLO failed, try the simpler HELO command. */ - //if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) { - if (($error = $this->_put('HELO', $this->localhost)) === false) { - return $error; - } - //if (PEAR::isError($this->_parseResponse(250))) { - if (($this->_parseResponse(250)) === false) { - return Net_SMTP::raiseError('HELO was not accepted: ', $this->_code, -// PEAR_ERROR_RETURN); - 1); - } - - return true; - } - - foreach ($this->_arguments as $argument) { - $verb = strtok($argument, ' '); - $arguments = substr($argument, strlen($verb) + 1, - strlen($argument) - strlen($verb) - 1); - $this->_esmtp[$verb] = $arguments; - } - - if (!isset($this->_esmtp['PIPELINING'])) { - $this->pipelining = false; - } - - return true; - } - - /** - * Returns the name of the best authentication method that the server - * has advertised. - * - * @return mixed Returns a string containing the name of the best - * supported authentication method. - * @throws PEAR_Exception - */ - protected function _getBestAuthMethod() - { - $available_methods = explode(' ', $this->_esmtp['AUTH']); - - foreach ($this->auth_methods as $method => $callback) { - if (in_array($method, $available_methods)) { - return $method; - } - } - - return Net_SMTP::raiseError('No supported authentication methods', -// null, PEAR_ERROR_RETURN); - null, 1); - } - - /** - * Attempt to do SMTP authentication. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $method The requested authentication method. If none is - * specified, the best supported method will be - * used. - * @param bool $tls Flag indicating whether or not TLS should be - * attempted. - * @param string $authz An optional authorization identifier. If - * specified, this identifier will be used as the - * authorization proxy. - * - * @throws PEAR_Exception - */ - public function auth($uid, $pwd, $method = '', $tls = true, $authz = '') - { - /* We can only attempt a TLS connection if one has been requested, - * we're running PHP 5.1.0 or later, have access to the OpenSSL - * extension, are connected to an SMTP server which supports the - * STARTTLS extension, and aren't already connected over a secure - * (SSL) socket connection. */ - if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') && - extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) && - strncasecmp($this->host, 'ssl://', 6) !== 0) { - /* Start the TLS connection attempt. */ - //if (PEAR::isError($result = $this->_put('STARTTLS'))) { - if (($result = $this->_put('STARTTLS')) === false) { - return $result; - } - //if (PEAR::isError($result = $this->_parseResponse(220))) { - if (($result = $this->_parseResponse(220)) === false) { - return $result; - } - //if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) { - if (($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT, $this->_verify_peer, $this->_verify_peer_name, $this->_allow_self_signed)) === false) { - return $result; - } elseif ($result !== true) { - return Net_SMTP::raiseError('STARTTLS failed'); - } - - /* Send EHLO again to recieve the AUTH string from the - * SMTP server. */ - $this->_negotiate(); - } - - if (empty($this->_esmtp['AUTH'])) { - return Net_SMTP::raiseError('SMTP server does not support authentication'); - } - - /* If no method has been specified, get the name of the best - * supported method advertised by the SMTP server. */ - if (empty($method)) { - //if (PEAR::isError($method = $this->_getBestAuthMethod())) { - if (($method = $this->_getBestAuthMethod()) === false) { - return $method; - } - } else { - $method = strtoupper($method); - } - - if (!array_key_exists($method, $this->auth_methods)) { - return Net_SMTP::raiseError("$method is not a supported authentication method"); - } - - if (!is_callable($this->auth_methods[$method], false)) { - return Net_SMTP::raiseError("$method authentication method cannot be called"); - } - - if (is_array($this->auth_methods[$method])) { - list($object, $method) = $this->auth_methods[$method]; - $result = $object->{$method}($uid, $pwd, $authz, $this); - } else { - $func = $this->auth_methods[$method]; - $result = $func($uid, $pwd, $authz, $this); - } - - /* If an error was encountered, return the PEAR_Error object. */ - //if (PEAR::isError($result)) { - if ($result === false) { - return $result; - } - - return true; - } - - /** - * Add a new authentication method. - * - * @param string $name The authentication method name (e.g. 'PLAIN') - * @param mixed $callback The authentication callback (given as the name - * of a function or as an (object, method name) - * array). - * @param bool $prepend Should the new method be prepended to the list - * of available methods? This is the default - * behavior, giving the new method the highest - * priority. - * - * @throws PEAR_Exception - */ - public function setAuthMethod($name, $callback, $prepend = true) - { - if (!is_string($name)) { - return Net_SMTP::raiseError('Method name is not a string'); - } - - if (!is_string($callback) && !is_array($callback)) { - return Net_SMTP::raiseError('Method callback must be string or array'); - } - - if (is_array($callback) && - (!is_object($callback[0]) || !is_string($callback[1]))) { - return Net_SMTP::raiseError('Bad mMethod callback array'); - } - - if ($prepend) { - $this->auth_methods = array_merge(array($name => $callback), - $this->auth_methods); - } else { - $this->auth_methods[$name] = $callback; - } - - return true; - } - - /** - * Authenticates the user using the DIGEST-MD5 method. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $authz The optional authorization proxy identifier. - * - * @throws PEAR_Exception - */ - protected function _authDigest_MD5($uid, $pwd, $authz = '') - { - //if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) { - if (($error = $this->_put('AUTH', 'DIGEST-MD5')) === false) { - return $error; - } - - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $challenge = base64_decode($this->_arguments[0]); - $digest = Auth_SASL::factory('digest-md5'); - $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, - $this->host, "smtp", - $authz)); - - //if (PEAR::isError($error = $this->_put($auth_str))) { - if (($error = $this->_put($auth_str)) === false) { - return $error; - } - - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - return $error; - } - - /* We don't use the protocol's third step because SMTP doesn't - * allow subsequent authentication, so we just silently ignore - * it. */ - //if (PEAR::isError($error = $this->_put(''))) { - if (($error = $this->_put('')) === false) { - return $error; - } - - /* 235: Authentication successful */ - //if (PEAR::isError($error = $this->_parseResponse(235))) { - if (($error = $this->_parseResponse(235)) === false) { - return $error; - } - - return true; - } - - /** - * Authenticates the user using the CRAM-MD5 method. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $authz The optional authorization proxy identifier. - * - * @throws PEAR_Exception - */ - protected function _authCRAM_MD5($uid, $pwd, $authz = '') - { - //if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) { - if (($error = $this->_put('AUTH', 'CRAM-MD5')) === false) { - return $error; - } - - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $challenge = base64_decode($this->_arguments[0]); - $cram = Auth_SASL::factory('cram-md5'); - $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); - - //if (PEAR::isError($error = $this->_put($auth_str))) { - if (($error = $this->_put($auth_str)) === false) { - return $error; - } - - /* 235: Authentication successful */ - //if (PEAR::isError($error = $this->_parseResponse(235))) { - if (($error = $this->_parseResponse(235)) === false) { - return $error; - } - - return true; - } - - /** - * Authenticates the user using the LOGIN method. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $authz The optional authorization proxy identifier. - * - * @throws PEAR_Exception - */ - protected function _authLogin($uid, $pwd, $authz = '') - { - //if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) { - if (($error = $this->_put('AUTH', 'LOGIN')) === false) { - return $error; - } - - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - //if (PEAR::isError($error = $this->_put(base64_encode($uid)))) { - if (($error = $this->_put(base64_encode($uid))) === false) { - return $error; - } - - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - return $error; - } - - //if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) { - if (($error = $this->_put(base64_encode($pwd))) === false) { - return $error; - } - - /* 235: Authentication successful */ - //if (PEAR::isError($error = $this->_parseResponse(235))) { - if (($error = $this->_parseResponse(235)) === false) { - return $error; - } - - return true; - } - - /** - * Authenticates the user using the PLAIN method. - * - * @param string $uid The userid to authenticate as. - * @param string $pwd The password to authenticate with. - * @param string $authz The optional authorization proxy identifier. - * - * @throws PEAR_Exception - */ - protected function _authPlain($uid, $pwd, $authz = '') - { - //if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) { - if (($error = $this->_put('AUTH', 'PLAIN')) === false) { - return $error; - } - /* 334: Continue authentication request */ - //if (PEAR::isError($error = $this->_parseResponse(334))) { - if (($error = $this->_parseResponse(334)) === false) { - /* 503: Error: already authenticated */ - if ($this->_code === 503) { - return true; - } - return $error; - } - - $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd); - - //if (PEAR::isError($error = $this->_put($auth_str))) { - if (($error = $this->_put($auth_str)) === false) { - return $error; - } - - /* 235: Authentication successful */ - //if (PEAR::isError($error = $this->_parseResponse(235))) { - if (($error = $this->_parseResponse(235)) === false) { - return $error; - } - - return true; - } - - /** - * Send the HELO command. - * - * @param string The domain name to say we are. - * - * @throws PEAR_Exception - */ - public function helo($domain) - { - //if (PEAR::isError($error = $this->_put('HELO', $domain))) { - if (($error = $this->_put('HELO', $domain)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250))) { - if (($error = $this->_parseResponse(250)) === false) { - return $error; - } - - return true; - } - - /** - * Return the list of SMTP service extensions advertised by the server. - * - * @return array The list of SMTP service extensions. - */ - public function getServiceExtensions() - { - return $this->_esmtp; - } - - /** - * Send the MAIL FROM: command. - * - * @param string $sender The sender (reverse path) to set. - * @param string $params String containing additional MAIL parameters, - * such as the NOTIFY flags defined by RFC 1891 - * or the VERP protocol. - * - * @throws PEAR_Exception - */ - public function mailFrom($sender, $params = null) - { - $args = "FROM:<$sender>"; - if (is_string($params) && strlen($params)) { - $args .= ' ' . $params; - } - - //if (PEAR::isError($error = $this->_put('MAIL', $args))) { - if (($error = $this->_put('MAIL', $args)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the RCPT TO: command. - * - * @param string $recipient The recipient (forward path) to add. - * @param string $params String containing additional RCPT parameters, - * such as the NOTIFY flags defined by RFC 1891. - * - * @throws PEAR_Exception - */ - public function rcptTo($recipient, $params = null) - { - $args = "TO:<$recipient>"; - if (is_string($params) && strlen($params)) { - $args .= ' ' . $params; - } - - //if (PEAR::isError($error = $this->_put('RCPT', $args))) { - if (($error = $this->_put('RCPT', $args)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) { - if (($error = $this->_parseResponse(array(250, 251), $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Quote the data so that it meets SMTP standards. - * - * This is provided as a separate public function to facilitate - * easier overloading for the cases where it is desirable to - * customize the quoting behavior. - * - * @param string &$data The message text to quote. The string must be - * passed by reference, and the text will be - * modified in place. - */ - public function quotedata(&$data) - { - /* Because a single leading period (.) signifies an end to the - * data, legitimate leading periods need to be "doubled" ('..'). - * Also: change Unix (\n) and Mac (\r) linefeeds into CRLF's - * (\r\n). */ - $data = preg_replace( - array('/^\./m', '/(?:\r\n|\n|\r(?!\n))/'), - array('..', "\r\n"), - $data - ); - } - - /** - * Send the DATA command. - * - * @param mixed $data The message data, either as a string or an open - * file resource. - * @param string $headers The message headers. If $headers is provided, - * $data is assumed to contain only body data. - * - * @throws PEAR_Exception - */ - public function data($data, $headers = null) - { - /* Verify that $data is a supported type. */ - if (!is_string($data) && !is_resource($data)) { - return Net_SMTP::raiseError('Expected a string or file resource'); - } - - /* Start by considering the size of the optional headers string. We - * also account for the addition 4 character "\r\n\r\n" separator - * sequence. */ - $size = is_null($headers) ? 0 : strlen($headers) + 4; - - if (is_resource($data)) { - $stat = fstat($data); - if ($stat === false) { - return Net_SMTP::raiseError('Failed to get file size'); - } - $size += $stat['size']; - } else { - $size += strlen($data); - } - - /* RFC 1870, section 3, subsection 3 states "a value of zero indicates - * that no fixed maximum message size is in force". Furthermore, it - * says that if "the parameter is omitted no information is conveyed - * about the server's fixed maximum message size". */ - $limit = isset($this->_esmtp['SIZE']) ? $this->_esmtp['SIZE'] : 0; - if ($limit > 0 && $size >= $limit) { - $this->disconnect(); - return Net_SMTP::raiseError('Message size exceeds server limit'); - } - - /* Initiate the DATA command. */ - //if (PEAR::isError($error = $this->_put('DATA'))) { - if (($error = $this->_put('DATA')) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(354))) { - if (($error = $this->_parseResponse(354)) === false) { - return $error; - } - - /* If we have a separate headers string, send it first. */ - if (!is_null($headers)) { - $this->quotedata($headers); - //if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) { - if (($result = $this->_send($headers . "\r\n\r\n")) === false) { - return $result; - } - } - - /* Now we can send the message body data. */ - if (is_resource($data)) { - /* Stream the contents of the file resource out over our socket - * connection, line by line. Each line must be run through the - * quoting routine. */ - while (strlen($line = fread($data, 8192)) > 0) { - /* If the last character is an newline, we need to grab the - * next character to check to see if it is a period. */ - while (!feof($data)) { - $char = fread($data, 1); - $line .= $char; - if ($char != "\n") { - break; - } - } - $this->quotedata($line); - //if (PEAR::isError($result = $this->_send($line))) { - if (($result = $this->_send($line)) === false) { - return $result; - } - } - } else { - /* Break up the data by sending one chunk (up to 512k) at a time. - * This approach reduces our peak memory usage. */ - for ($offset = 0; $offset < $size;) { - $end = $offset + 512000; - - /* Ensure we don't read beyond our data size or span multiple - * lines. quotedata() can't properly handle character data - * that's split across two line break boundaries. */ - if ($end >= $size) { - $end = $size; - } else { - for (; $end < $size; $end++) { - if ($data[$end] != "\n") { - break; - } - } - } - - /* Extract our chunk and run it through the quoting routine. */ - $chunk = substr($data, $offset, $end - $offset); - $this->quotedata($chunk); - - /* If we run into a problem along the way, abort. */ - //if (PEAR::isError($result = $this->_send($chunk))) { - if (($result = $this->_send($chunk)) === false) { - return $result; - } - - /* Advance the offset to the end of this chunk. */ - $offset = $end; - } - } - - /* Finally, send the DATA terminator sequence. */ - //if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) { - if (($result = $this->_send("\r\n.\r\n")) === false) { - return $result; - } - - /* Verify that the data was successfully received by the server. */ - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the SEND FROM: command. - * - * @param string $path The reverse path to send. - * - * @throws PEAR_Exception - */ - public function sendFrom($path) - { - //if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) { - if (($error = $this->_put('SEND', "FROM:<$path>")) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the SOML FROM: command. - * - * @param string $path The reverse path to send. - * - * @throws PEAR_Exception - */ - public function somlFrom($path) - { - //if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) { - if (($error = $this->_put('SOML', "FROM:<$path>")) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the SAML FROM: command. - * - * @param string $path The reverse path to send. - * - * @throws PEAR_Exception - */ - public function samlFrom($path) - { - //if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) { - if (($error = $this->_put('SAML', "FROM:<$path>")) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the RSET command. - * - * @throws PEAR_Exception - */ - public function rset() - { - //if (PEAR::isError($error = $this->_put('RSET'))) { - if (($error = $this->_put('RSET')) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) { - if (($error = $this->_parseResponse(250, $this->pipelining)) === false) { - return $error; - } - - return true; - } - - /** - * Send the VRFY command. - * - * @param string $string The string to verify - * - * @throws PEAR_Exception - */ - public function vrfy($string) - { - /* Note: 251 is also a valid response code */ - //if (PEAR::isError($error = $this->_put('VRFY', $string))) { - if (($error = $this->_put('VRFY', $string)) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) { - if (($error = $this->_parseResponse(array(250, 252))) === false) { - return $error; - } - - return true; - } - - /** - * Send the NOOP command. - * - * @throws PEAR_Exception - */ - public function noop() - { - //if (PEAR::isError($error = $this->_put('NOOP'))) { - if (($error = $this->_put('NOOP')) === false) { - return $error; - } - //if (PEAR::isError($error = $this->_parseResponse(250))) { - if (($error = $this->_parseResponse(250)) === false) { - return $error; - } - - return true; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - static function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Net_SMTP error: ". $message); - return false; - } -} diff --git a/sources/include/Net/Socket.php b/sources/include/Net/Socket.php deleted file mode 100644 index 97e3fab..0000000 --- a/sources/include/Net/Socket.php +++ /dev/null @@ -1,726 +0,0 @@ - - * Chuck Hagenbuch - * - * @category Net - * @package Net_Socket - * @author Stig Bakken - * @author Chuck Hagenbuch - * @copyright 1997-2003 The PHP Group - * @license http://www.php.net/license/2_02.txt PHP 2.02 - * @link http://pear.php.net/packages/Net_Socket - */ - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError(), and defining OS_WINDOWS - * - * Reference implementation used: - * http://download.pear.php.net/package/Net_Socket-1.0.14.tgz - * - * - */ - -//require_once 'PEAR.php'; - -if (substr(PHP_OS, 0, 3) == 'WIN') { - define('OS_WINDOWS', true); -} else { - define('OS_WINDOWS', false); -} - -define('NET_SOCKET_READ', 1); -define('NET_SOCKET_WRITE', 2); -define('NET_SOCKET_ERROR', 4); - -/** - * Generalized Socket class. - * - * @category Net - * @package Net_Socket - * @author Stig Bakken - * @author Chuck Hagenbuch - * @copyright 1997-2003 The PHP Group - * @license http://www.php.net/license/2_02.txt PHP 2.02 - * @link http://pear.php.net/packages/Net_Socket - */ -//class Net_Socket extends PEAR -class Net_Socket -{ - /** - * Socket file pointer. - * @var resource $fp - */ - var $fp = null; - - /** - * Whether the socket is blocking. Defaults to true. - * @var boolean $blocking - */ - var $blocking = true; - - /** - * Whether the socket is persistent. Defaults to false. - * @var boolean $persistent - */ - var $persistent = false; - - /** - * The IP address to connect to. - * @var string $addr - */ - var $addr = ''; - - /** - * The port number to connect to. - * @var integer $port - */ - var $port = 0; - - /** - * Number of seconds to wait on socket operations before assuming - * there's no more data. Defaults to no timeout. - * @var integer|float $timeout - */ - var $timeout = null; - - /** - * Number of bytes to read at a time in readLine() and - * readAll(). Defaults to 2048. - * @var integer $lineLength - */ - var $lineLength = 2048; - - /** - * The string to use as a newline terminator. Usually "\r\n" or "\n". - * @var string $newline - */ - var $newline = "\r\n"; - - /** - * Connect to the specified port. If called when the socket is - * already connected, it disconnects and connects again. - * - * @param string $addr IP address or host name (may be with protocol prefix). - * @param integer $port TCP port number. - * @param boolean $persistent (optional) Whether the connection is - * persistent (kept open between requests - * by the web server). - * @param integer $timeout (optional) Connection socket timeout. - * @param array $options See options for stream_context_create. - * - * @access public - * - * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. - */ - function connect($addr, $port = 0, $persistent = null, - $timeout = null, $options = null) - { - if (is_resource($this->fp)) { - @fclose($this->fp); - $this->fp = null; - } - - if (!$addr) { - return $this->raiseError('$addr cannot be empty'); - } else if (strspn($addr, ':.0123456789') == strlen($addr)) { - $this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr; - } else { - $this->addr = $addr; - } - - $this->port = $port % 65536; - - if ($persistent !== null) { - $this->persistent = $persistent; - } - - $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen'; - $errno = 0; - $errstr = ''; - - $old_track_errors = @ini_set('track_errors', 1); - - if ($timeout <= 0) { - $timeout = @ini_get('default_socket_timeout'); - } - - if ($options && function_exists('stream_context_create')) { - $context = stream_context_create($options); - - // Since PHP 5 fsockopen doesn't allow context specification - if (function_exists('stream_socket_client')) { - $flags = STREAM_CLIENT_CONNECT; - - if ($this->persistent) { - $flags = STREAM_CLIENT_PERSISTENT; - } - - $addr = $this->addr . ':' . $this->port; - $fp = stream_socket_client($addr, $errno, $errstr, - $timeout, $flags, $context); - } else { - $fp = @$openfunc($this->addr, $this->port, $errno, - $errstr, $timeout, $context); - } - } else { - $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout); - } - - if (!$fp) { - if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) { - $errstr = $php_errormsg; - } - @ini_set('track_errors', $old_track_errors); - return $this->raiseError($errstr, $errno); - } - - @ini_set('track_errors', $old_track_errors); - $this->fp = $fp; - $this->setTimeout(); - return $this->setBlocking($this->blocking); - } - - /** - * Disconnects from the peer, closes the socket. - * - * @access public - * @return mixed true on success or a PEAR_Error instance otherwise - */ - function disconnect() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - @fclose($this->fp); - $this->fp = null; - return true; - } - - /** - * Set the newline character/sequence to use. - * - * @param string $newline Newline character(s) - * @return boolean True - */ - function setNewline($newline) - { - $this->newline = $newline; - return true; - } - - /** - * Find out if the socket is in blocking mode. - * - * @access public - * @return boolean The current blocking mode. - */ - function isBlocking() - { - return $this->blocking; - } - - /** - * Sets whether the socket connection should be blocking or - * not. A read call to a non-blocking socket will return immediately - * if there is no data available, whereas it will block until there - * is data for blocking sockets. - * - * @param boolean $mode True for blocking sockets, false for nonblocking. - * - * @access public - * @return mixed true on success or a PEAR_Error instance otherwise - */ - function setBlocking($mode) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $this->blocking = $mode; - stream_set_blocking($this->fp, (int)$this->blocking); - return true; - } - - /** - * Sets the timeout value on socket descriptor, - * expressed in the sum of seconds and microseconds - * - * @param integer $seconds Seconds. - * @param integer $microseconds Microseconds, optional. - * - * @access public - * @return mixed True on success or false on failure or - * a PEAR_Error instance when not connected - */ - function setTimeout($seconds = null, $microseconds = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if ($seconds === null && $microseconds === null) { - $seconds = (int) $this->timeout; - $microseconds = (int) (($this->timeout - $seconds) * 1000000); - } else { - $this->timeout = $seconds + $microseconds/1000000; - } - - if ($this->timeout > 0) { - return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds); - } - else { - return false; - } - } - - /** - * Sets the file buffering size on the stream. - * See php's stream_set_write_buffer for more information. - * - * @param integer $size Write buffer size. - * - * @access public - * @return mixed on success or an PEAR_Error object otherwise - */ - function setWriteBuffer($size) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $returned = stream_set_write_buffer($this->fp, $size); - if ($returned == 0) { - return true; - } - return $this->raiseError('Cannot set write buffer.'); - } - - /** - * Returns information about an existing socket resource. - * Currently returns four entries in the result array: - * - *

- * timed_out (bool) - The socket timed out waiting for data
- * blocked (bool) - The socket was blocked
- * eof (bool) - Indicates EOF event
- * unread_bytes (int) - Number of bytes left in the socket buffer
- *

- * - * @access public - * @return mixed Array containing information about existing socket - * resource or a PEAR_Error instance otherwise - */ - function getStatus() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return stream_get_meta_data($this->fp); - } - - /** - * Get a specified line of data - * - * @param int $size Reading ends when size - 1 bytes have been read, - * or a newline or an EOF (whichever comes first). - * If no size is specified, it will keep reading from - * the stream until it reaches the end of the line. - * - * @access public - * @return mixed $size bytes of data from the socket, or a PEAR_Error if - * not connected. If an error occurs, FALSE is returned. - */ - function gets($size = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if (is_null($size)) { - return @fgets($this->fp); - } else { - return @fgets($this->fp, $size); - } - } - - /** - * Read a specified amount of data. This is guaranteed to return, - * and has the added benefit of getting everything in one fread() - * chunk; if you know the size of the data you're getting - * beforehand, this is definitely the way to go. - * - * @param integer $size The number of bytes to read from the socket. - * - * @access public - * @return $size bytes of data from the socket, or a PEAR_Error if - * not connected. - */ - function read($size) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return @fread($this->fp, $size); - } - - /** - * Write a specified amount of data. - * - * @param string $data Data to write. - * @param integer $blocksize Amount of data to write at once. - * NULL means all at once. - * - * @access public - * @return mixed If the socket is not connected, returns an instance of - * PEAR_Error. - * If the write succeeds, returns the number of bytes written. - * If the write fails, returns false. - * If the socket times out, returns an instance of PEAR_Error. - */ - function write($data, $blocksize = null) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - if (is_null($blocksize) && !OS_WINDOWS) { - $written = @fwrite($this->fp, $data); - - // Check for timeout or lost connection - if (!$written) { - $meta_data = $this->getStatus(); - - if (!is_array($meta_data)) { - return $meta_data; // PEAR_Error - } - - if (!empty($meta_data['timed_out'])) { - return $this->raiseError('timed out'); - } - } - - return $written; - } else { - if (is_null($blocksize)) { - $blocksize = 1024; - } - - $pos = 0; - $size = strlen($data); - while ($pos < $size) { - $written = @fwrite($this->fp, substr($data, $pos, $blocksize)); - - // Check for timeout or lost connection - if (!$written) { - $meta_data = $this->getStatus(); - - if (!is_array($meta_data)) { - return $meta_data; // PEAR_Error - } - - if (!empty($meta_data['timed_out'])) { - return $this->raiseError('timed out'); - } - - return $written; - } - - $pos += $written; - } - - return $pos; - } - } - - /** - * Write a line of data to the socket, followed by a trailing newline. - * - * @param string $data Data to write - * - * @access public - * @return mixed fwrite() result, or PEAR_Error when not connected - */ - function writeLine($data) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return fwrite($this->fp, $data . $this->newline); - } - - /** - * Tests for end-of-file on a socket descriptor. - * - * Also returns true if the socket is disconnected. - * - * @access public - * @return bool - */ - function eof() - { - return (!is_resource($this->fp) || feof($this->fp)); - } - - /** - * Reads a byte of data - * - * @access public - * @return 1 byte of data from the socket, or a PEAR_Error if - * not connected. - */ - function readByte() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - return ord(@fread($this->fp, 1)); - } - - /** - * Reads a word of data - * - * @access public - * @return 1 word of data from the socket, or a PEAR_Error if - * not connected. - */ - function readWord() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 2); - return (ord($buf[0]) + (ord($buf[1]) << 8)); - } - - /** - * Reads an int of data - * - * @access public - * @return integer 1 int of data from the socket, or a PEAR_Error if - * not connected. - */ - function readInt() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 4); - return (ord($buf[0]) + (ord($buf[1]) << 8) + - (ord($buf[2]) << 16) + (ord($buf[3]) << 24)); - } - - /** - * Reads a zero-terminated string of data - * - * @access public - * @return string, or a PEAR_Error if - * not connected. - */ - function readString() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $string = ''; - while (($char = @fread($this->fp, 1)) != "\x00") { - $string .= $char; - } - return $string; - } - - /** - * Reads an IP Address and returns it in a dot formatted string - * - * @access public - * @return Dot formatted string, or a PEAR_Error if - * not connected. - */ - function readIPAddress() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $buf = @fread($this->fp, 4); - return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]), - ord($buf[2]), ord($buf[3])); - } - - /** - * Read until either the end of the socket or a newline, whichever - * comes first. Strips the trailing newline from the returned data. - * - * @access public - * @return All available data up to a newline, without that - * newline, or until the end of the socket, or a PEAR_Error if - * not connected. - */ - function readLine() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $line = ''; - - $timeout = time() + $this->timeout; - - while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) { - $line .= @fgets($this->fp, $this->lineLength); - if (substr($line, -1) == "\n") { - return rtrim($line, $this->newline); - } - } - return $line; - } - - /** - * Read until the socket closes, or until there is no more data in - * the inner PHP buffer. If the inner buffer is empty, in blocking - * mode we wait for at least 1 byte of data. Therefore, in - * blocking mode, if there is no data at all to be read, this - * function will never exit (unless the socket is closed on the - * remote end). - * - * @access public - * - * @return string All data until the socket closes, or a PEAR_Error if - * not connected. - */ - function readAll() - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $data = ''; - while (!feof($this->fp)) { - $data .= @fread($this->fp, $this->lineLength); - } - return $data; - } - - /** - * Runs the equivalent of the select() system call on the socket - * with a timeout specified by tv_sec and tv_usec. - * - * @param integer $state Which of read/write/error to check for. - * @param integer $tv_sec Number of seconds for timeout. - * @param integer $tv_usec Number of microseconds for timeout. - * - * @access public - * @return False if select fails, integer describing which of read/write/error - * are ready, or PEAR_Error if not connected. - */ - function select($state, $tv_sec, $tv_usec = 0) - { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - $read = null; - $write = null; - $except = null; - if ($state & NET_SOCKET_READ) { - $read[] = $this->fp; - } - if ($state & NET_SOCKET_WRITE) { - $write[] = $this->fp; - } - if ($state & NET_SOCKET_ERROR) { - $except[] = $this->fp; - } - if (false === ($sr = stream_select($read, $write, $except, - $tv_sec, $tv_usec))) { - return false; - } - - $result = 0; - if (count($read)) { - $result |= NET_SOCKET_READ; - } - if (count($write)) { - $result |= NET_SOCKET_WRITE; - } - if (count($except)) { - $result |= NET_SOCKET_ERROR; - } - return $result; - } - - /** - * Turns encryption on/off on a connected socket. - * - * @param bool $enabled Set this parameter to true to enable encryption - * and false to disable encryption. - * @param integer $type Type of encryption. See stream_socket_enable_crypto() - * for values. - * @param boolean $verify_peer Require verification of SSL certificate used - * @param boolean $verify_peer_name Require verification of peer name - * @param boolean $allow_self_signed Allow self-signed certificates. Requires verify_peer - * - * @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php - * @access public - * @return false on error, true on success and 0 if there isn't enough data - * and the user should try again (non-blocking sockets only). - * A PEAR_Error object is returned if the socket is not - * connected - */ - function enableCrypto($enabled, $type, $verify_peer, $verify_peer_name, $allow_self_signed) - { - if (version_compare(phpversion(), "5.1.0", ">=")) { - if (!is_resource($this->fp)) { - return $this->raiseError('not connected'); - } - - // 5.6.0 Added verify_peer_name. verify_peer default changed to TRUE. - if (version_compare(phpversion(), "5.6.0", ">=")) - stream_context_set_option($this->fp, array('ssl' => array('verify_peer' => $verify_peer, 'verify_peer_name' => $verify_peer_name, 'allow_self_signed' => $allow_self_signed))); - else - stream_context_set_option($this->fp, array('ssl' => array('verify_peer' => $verify_peer, 'allow_self_signed' => $allow_self_signed))); - - return @stream_socket_enable_crypto($this->fp, $enabled, $type); - } else { - $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0'; - return $this->raiseError($msg); - } - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "Net_Socket error: ". $message); - return false; - } -} diff --git a/sources/include/iCalendar.php b/sources/include/iCalendar.php deleted file mode 100644 index a84fbbc..0000000 --- a/sources/include/iCalendar.php +++ /dev/null @@ -1,1799 +0,0 @@ -component will point to the wrapping VCALENDAR component of -* the iCalendar. This will be fine for simple iCalendar usage as sampled below, -* but more complex iCalendar such as a VEVENT with RRULE which has repeat overrides -* will need quite a bit more thought to process correctly. -* -* @example -* To create a new iCalendar from several data values: -* $ical = new iCalendar( array('DTSTART' => $dtstart, 'SUMMARY' => $summary, 'DURATION' => $duration ) ); -* -* @example -* To render it as an iCalendar string: -* echo $ical->Render(); -* -* @example -* To render just the VEVENTs in the iCalendar with a restricted list of properties: -* echo $ical->Render( false, 'VEVENT', array( 'DTSTART', 'DURATION', 'DTEND', 'RRULE', 'SUMMARY') ); -* -* @example -* To parse an existing iCalendar string for manipulation: -* $ical = new iCalendar( array('icalendar' => $icalendar_text ) ); -* -* @example -* To clear any 'VALARM' components in an iCalendar object -* $ical->component->ClearComponents('VALARM'); -* -* @example -* To replace any 'RRULE' property in an iCalendar object -* $ical->component->SetProperties( 'RRULE', $rrule_definition ); -* -* @package awl -* @subpackage iCalendar -* @author Andrew McMillan -* @copyright Catalyst IT Ltd, Morphoss Ltd -* @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later -* -*/ -require_once('XMLElement.php'); -/* Commented out, only needed by deprecated functions -require_once('AwlQuery.php'); -*/ - -/** -* A Class for representing properties within an iCalendar -* -* @package awl -*/ -class iCalProp { - /**#@+ - * @access private - */ - - /** - * The name of this property - * - * @var string - */ - var $name; - - /** - * An array of parameters to this property, represented as key/value pairs. - * - * @var array - */ - var $parameters; - - /** - * The value of this property. - * - * @var string - */ - var $content; - - /** - * The original value that this was parsed from, if that's the way it happened. - * - * @var string - */ - var $rendered; - - /**#@-*/ - - /** - * The constructor parses the incoming string, which is formatted as per RFC2445 as a - * propname[;param1=pval1[; ... ]]:propvalue - * however we allow ourselves to assume that the RFC2445 content unescaping has already - * happened when iCalComponent::ParseFrom() called iCalComponent::UnwrapComponent(). - * - * @param string $propstring The string from the iCalendar which contains this property. - */ - function iCalProp( $propstring = null ) { - $this->name = ""; - $this->content = ""; - $this->parameters = array(); - unset($this->rendered); - if ( $propstring != null && gettype($propstring) == 'string' ) { - $this->ParseFrom($propstring); - } - } - - - /** - * The constructor parses the incoming string, which is formatted as per RFC2445 as a - * propname[;param1=pval1[; ... ]]:propvalue - * however we allow ourselves to assume that the RFC2445 content unescaping has already - * happened when iCalComponent::ParseFrom() called iCalComponent::UnwrapComponent(). - * - * @param string $propstring The string from the iCalendar which contains this property. - */ - function ParseFrom( $propstring ) { -// $this->rendered = (strlen($propstring) < 72 ? $propstring : null); // Only pre-rendered if we didn't unescape it - // FMBIETE - unset rendered content; if we alter some properties inside an object (VEVENT/ATTENDEE for example) we won't see the changes calling Render - // FIXME: if you find the bug, let me know - //$ical = new iCalComponent(); - //$ical->ParseFrom(VCALENDAR DATA); - // Doing this will refresh the rendered data, but if this line is not executed, you won't see PARTSTAT changed - //$ical->SetPValue("METHOD", "REPLY"); - //$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", "ACCEPTED"); - //printf("%s\n", $ical->Render()); - unset($this->rendered); - - $unescaped = preg_replace( '{\\\\[nN]}', "\n", $propstring); - - // Split into two parts on : which is not preceded by a \ - list( $start, $values) = preg_split( '{(?content = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $values); - - // Split on ; which is not preceded by a \ - $parameters = preg_split( '{(?name = array_shift( $parameters ); - $this->parameters = array(); - foreach( $parameters AS $k => $v ) { - $pos = strpos($v,'='); - $name = substr( $v, 0, $pos); - $value = substr( $v, $pos + 1); - $this->parameters[$name] = $value; - } -// dbg_error_log('iCalendar', " iCalProp::ParseFrom found '%s' = '%s' with %d parameters", $this->name, substr($this->content,0,200), count($this->parameters) ); - } - - - /** - * Get/Set name property - * - * @param string $newname [optional] A new name for the property - * - * @return string The name for the property. - */ - function Name( $newname = null ) { - if ( $newname != null ) { - $this->name = $newname; - if ( isset($this->rendered) ) unset($this->rendered); -// dbg_error_log('iCalendar', " iCalProp::Name(%s)", $this->name ); - } - return $this->name; - } - - - /** - * Get/Set the content of the property - * - * @param string $newvalue [optional] A new value for the property - * - * @return string The value of the property. - */ - function Value( $newvalue = null ) { - if ( $newvalue != null ) { - $this->content = $newvalue; - if ( isset($this->rendered) ) unset($this->rendered); - } - return $this->content; - } - - - /** - * Get/Set parameters in their entirety - * - * @param array $newparams An array of new parameter key/value pairs - * - * @return array The current array of parameters for the property. - */ - function Parameters( $newparams = null ) { - if ( $newparams != null ) { - $this->parameters = $newparams; - if ( isset($this->rendered) ) unset($this->rendered); - } - return $this->parameters; - } - - - /** - * Test if our value contains a string - * - * @param string $search The needle which we shall search the haystack for. - * - * @return string The name for the property. - */ - function TextMatch( $search ) { - if ( isset($this->content) ) { - return (stristr( $this->content, $search ) !== false); - } - return false; - } - - - /** - * Get the value of a parameter - * - * @param string $name The name of the parameter to retrieve the value for - * - * @return string The value of the parameter - */ - function GetParameterValue( $name ) { - if ( isset($this->parameters[$name]) ) return $this->parameters[$name]; - } - - /** - * Set the value of a parameter - * - * @param string $name The name of the parameter to set the value for - * - * @param string $value The value of the parameter - */ - function SetParameterValue( $name, $value ) { - if ( isset($this->rendered) ) unset($this->rendered); - // Unset parameter - if ($value === null) { - unset($this->parameters[$name]); - } - else { - $this->parameters[$name] = $value; - } - } - - /** - * Render the set of parameters as key1=value1[;key2=value2[; ...]] with - * any colons or semicolons escaped. - */ - function RenderParameters() { - $rendered = ""; - foreach( $this->parameters AS $k => $v ) { - $escaped = preg_replace( "/([;:])/", '\\\\$1', $v); - $rendered .= sprintf( ";%s=%s", $k, $escaped ); - } - return $rendered; - } - - - /** - * Render a suitably escaped RFC2445 content string. - */ - function Render() { - // If we still have the string it was parsed in from, it hasn't been screwed with - // and we can just return that without modification. - if ( isset($this->rendered) ) return $this->rendered; - - $property = preg_replace( '/[;].*$/', '', $this->name ); - $escaped = $this->content; - switch( $property ) { - /** Content escaping does not apply to these properties culled from RFC2445 */ - case 'ATTACH': case 'GEO': case 'PERCENT-COMPLETE': case 'PRIORITY': - case 'DURATION': case 'FREEBUSY': case 'TZOFFSETFROM': case 'TZOFFSETTO': - case 'TZURL': case 'ATTENDEE': case 'ORGANIZER': case 'RECURRENCE-ID': - case 'URL': case 'EXRULE': case 'SEQUENCE': case 'CREATED': - case 'RRULE': case 'REPEAT': case 'TRIGGER': - break; - - case 'COMPLETED': case 'DTEND': - case 'DUE': case 'DTSTART': - case 'DTSTAMP': case 'LAST-MODIFIED': - case 'CREATED': case 'EXDATE': - case 'RDATE': - if ( isset($this->parameters['VALUE']) && $this->parameters['VALUE'] == 'DATE' ) { - $escaped = substr( $escaped, 0, 8); - } - break; - - /** Content escaping applies by default to other properties */ - default: - $escaped = str_replace( '\\', '\\\\', $escaped); - $escaped = preg_replace( '/\r?\n/', '\\n', $escaped); - $escaped = preg_replace( "/([,;])/", '\\\\$1', $escaped); - } - $property = sprintf( "%s%s:", $this->name, $this->RenderParameters() ); - if ( (strlen($property) + strlen($escaped)) <= 72 ) { - $this->rendered = $property . $escaped; - } - else if ( (strlen($property) + strlen($escaped)) > 72 && (strlen($property) < 72) && (strlen($escaped) < 72) ) { - $this->rendered = $property . "\r\n " . $escaped; - } - else { - $this->rendered = preg_replace( '/(.{72})/u', '$1'."\r\n ", $property . $escaped ); - } - return $this->rendered; - } - -} - - -/** -* A Class for representing components within an iCalendar -* -* @package awl -*/ -class iCalComponent { - /**#@+ - * @access private - */ - - /** - * The type of this component, such as 'VEVENT', 'VTODO', 'VTIMEZONE', etc. - * - * @var string - */ - var $type; - - /** - * An array of properties, which are iCalProp objects - * - * @var array - */ - var $properties; - - /** - * An array of (sub-)components, which are iCalComponent objects - * - * @var array - */ - var $components; - - /** - * The rendered result (or what was originally parsed, if there have been no changes) - * - * @var array - */ - var $rendered; - - /**#@-*/ - - /** - * A basic constructor - */ - function iCalComponent( $content = null ) { - $this->type = ""; - $this->properties = array(); - $this->components = array(); - $this->rendered = ""; - if ( $content != null && (gettype($content) == 'string' || gettype($content) == 'array') ) { - $this->ParseFrom($content); - } - } - - - /** - * Apply standard properties for a VCalendar - * @param array $extra_properties Key/value pairs of additional properties - */ - function VCalendar( $extra_properties = null ) { - $this->SetType('VCALENDAR'); - $this->AddProperty('PRODID', '-//davical.org//NONSGML AWL Calendar//EN'); - $this->AddProperty('VERSION', '2.0'); - $this->AddProperty('CALSCALE', 'GREGORIAN'); - if ( is_array($extra_properties) ) { - foreach( $extra_properties AS $k => $v ) { - $this->AddProperty($k,$v); - } - } - } - - /** - * Collect an array of all parameters of our properties which are the specified type - * Mainly used for collecting the full variety of references TZIDs - */ - function CollectParameterValues( $parameter_name ) { - $values = array(); - foreach( $this->components AS $k => $v ) { - $also = $v->CollectParameterValues($parameter_name); - $values = array_merge( $values, $also ); - } - foreach( $this->properties AS $k => $v ) { - $also = $v->GetParameterValue($parameter_name); - if ( isset($also) && $also != "" ) { -// dbg_error_log( 'iCalendar', "::CollectParameterValues(%s) : Found '%s'", $parameter_name, $also); - $values[$also] = 1; - } - } - return $values; - } - - - /** - * Parse the text $content into sets of iCalProp & iCalComponent within this iCalComponent - * @param string $content The raw RFC2445-compliant iCalendar component, including BEGIN:TYPE & END:TYPE - */ - function ParseFrom( $content ) { - $this->rendered = $content; - $content = $this->UnwrapComponent($content); - - $type = false; - $subtype = false; - $finish = null; - $subfinish = null; - - $length = strlen($content); - $linefrom = 0; - while( $linefrom < $length ) { - $lineto = strpos( $content, "\n", $linefrom ); - if ( $lineto === false ) { - $lineto = strpos( $content, "\r", $linefrom ); - } - if ( $lineto > 0 ) { - $line = substr( $content, $linefrom, $lineto - $linefrom); - $linefrom = $lineto + 1; - } - else { - $line = substr( $content, $linefrom ); - $linefrom = $length; - } - if ( preg_match('/^\s*$/', $line ) ) continue; - $line = rtrim( $line, "\r\n" ); -// dbg_error_log( 'iCalendar', "::ParseFrom: Parsing line: $line"); - - if ( $type === false ) { - if ( preg_match( '/^BEGIN:(.+)$/', $line, $matches ) ) { - // We have found the start of the main component - $type = $matches[1]; - $finish = "END:$type"; - $this->type = $type; - dbg_error_log( 'iCalendar', "::ParseFrom: Start component of type '%s'", $type); - } - else { - dbg_error_log( 'iCalendar', "::ParseFrom: Ignoring crap before start of component: $line"); - // unset($lines[$k]); // The content has crap before the start - if ( $line != "" ) $this->rendered = null; - } - } - else if ( $type == null ) { - dbg_error_log( 'iCalendar', "::ParseFrom: Ignoring crap after end of component"); - if ( $line != "" ) $this->rendered = null; - } - else if ( $line == $finish ) { - dbg_error_log( 'iCalendar', "::ParseFrom: End of component"); - $type = null; // We have reached the end of our component - } - else { - if ( $subtype === false && preg_match( '/^BEGIN:(.+)$/', $line, $matches ) ) { - // We have found the start of a sub-component - $subtype = $matches[1]; - $subfinish = "END:$subtype"; - $subcomponent = $line . "\r\n"; - dbg_error_log( 'iCalendar', "::ParseFrom: Found a subcomponent '%s'", $subtype); - } - else if ( $subtype ) { - // We are inside a sub-component - $subcomponent .= $this->WrapComponent($line); - if ( $line == $subfinish ) { - dbg_error_log( 'iCalendar', "::ParseFrom: End of subcomponent '%s'", $subtype); - // We have found the end of a sub-component - $this->components[] = new iCalComponent($subcomponent); - $subtype = false; - } -// else -// dbg_error_log( 'iCalendar', "::ParseFrom: Inside a subcomponent '%s'", $subtype ); - } - else { -// dbg_error_log( 'iCalendar', "::ParseFrom: Parse property of component"); - // It must be a normal property line within a component. - $this->properties[] = new iCalProp($line); - } - } - } - } - - - /** - * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According - * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising - * XML parsers often muck with it and may remove the CR. We accept either case. - */ - function UnwrapComponent( $content ) { - return preg_replace('/\r?\n[ \t]/', '', $content ); - } - - /** - * This imposes the (CRLF + linear space) wrapping specified in RFC2445. According - * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising - * XML parsers often muck with it and may remove the CR. We output RFC2445 compliance. - * - * In order to preserve pre-existing wrapping in the component, we split the incoming - * string on line breaks before running wordwrap over each component of that. - */ - function WrapComponent( $content ) { - $strs = preg_split( "/\r?\n/", $content ); - $wrapped = ""; - foreach ($strs as $str) { - $wrapped .= preg_replace( '/(.{72})/u', '$1'."\r\n ", $str ) ."\r\n"; - } - return $wrapped; - } - - /** - * Return the type of component which this is - */ - function GetType() { - return $this->type; - } - - - /** - * Set the type of component which this is - */ - function SetType( $type ) { - if ( isset($this->rendered) ) unset($this->rendered); - $this->type = $type; - return $this->type; - } - - - /** - * Get all properties, or the properties matching a particular type - */ - function GetProperties( $type = null ) { - $properties = array(); - foreach( $this->properties AS $k => $v ) { - if ( $type == null || $v->Name() == $type ) { - $properties[$k] = $v; - } - } - return $properties; - } - - - /** - * Get the value of the first property matching the name. Obviously this isn't - * so useful for properties which may occur multiply, but most don't. - * - * @param string $type The type of property we are after. - * @return string The value of the property, or null if there was no such property. - */ - function GetPValue( $type ) { - foreach( $this->properties AS $k => $v ) { - if ( $v->Name() == $type ) return $v->Value(); - } - return null; - } - - - /** - * Set the value of all properties matching the name. - * - * @param string $type The type/name of property we are after - * @param string $value The value of the property - */ - function SetPValue( $type, $value ) { - for ( $i = 0; $i < count($this->properties); $i++ ) { - if ( $this->properties[$i]->Name() == $type ) { - if ( isset($this->rendered) ) unset($this->rendered); - // FMBIETE - unset property - if ($value == null) { - unset($this->properties[$i]); - } - else { - $this->properties[$i]->Value($value); - } - } - } - } - - - /** - * Set the value of all the parameters matching the name. Component -> Property -> Parameter - * - * @param string $component_type Type of the component - * @param string $property_name Type/Name of the property - * @param string $parameter_name Type/Name of the parameter - * @param string $value New value of the parameter - * @param string $condition_value Change the parameter_value only if the property_value is equals to condition_value - */ - function SetCPParameterValue( $component_type, $property_name, $parameter_name, $value, $condition_value = null ) { - for ( $j = 0; $j < count($this->components); $j++ ) { - if ( $this->components[$j]->GetType() == $component_type ) { - for ( $i = 0; $i < count($this->components[$j]->properties); $i++ ) { - if ( $this->components[$j]->properties[$i]->Name() == $property_name ) { - if ( isset($this->components[$j]->rendered) ) unset($this->components[$j]->rendered); - if ($condition_value === null) { - $this->components[$j]->properties[$i]->SetParameterValue($parameter_name, $value); - } - else { - if (strcasecmp($this->components[$j]->properties[$i]->Value(), $condition_value) == 0) { - $this->components[$j]->properties[$i]->SetParameterValue($parameter_name, $value); - } - } - } - } - } - } - } - - - /** - * Get the value of the specified parameter for the first property matching the - * name. Obviously this isn't so useful for properties which may occur multiply, but most don't. - * - * @param string $type The type of property we are after. - * @param string $type The name of the parameter we are after. - * @return string The value of the parameter for the property, or null in the case that there was no such property, or no such parameter. - */ - function GetPParamValue( $type, $parameter_name ) { - foreach( $this->properties AS $k => $v ) { - if ( $v->Name() == $type ) return $v->GetParameterValue($parameter_name); - } - return null; - } - - - /** - * Clear all properties, or the properties matching a particular type - * @param string $type The type of property - omit for all properties - */ - function ClearProperties( $type = null ) { - if ( $type != null ) { - // First remove all the existing ones of that type - foreach( $this->properties AS $k => $v ) { - if ( $v->Name() == $type ) { - unset($this->properties[$k]); - if ( isset($this->rendered) ) unset($this->rendered); - } - } - $this->properties = array_values($this->properties); - } - else { - if ( isset($this->rendered) ) unset($this->rendered); - $this->properties = array(); - } - } - - - /** - * Set all properties, or the ones matching a particular type - */ - function SetProperties( $new_properties, $type = null ) { - if ( isset($this->rendered) && count($new_properties) > 0 ) unset($this->rendered); - $this->ClearProperties($type); - foreach( $new_properties AS $k => $v ) { - $this->AddProperty($v); - } - } - - - /** - * Adds a new property - * - * @param iCalProp $new_property The new property to append to the set, or a string with the name - * @param string $value The value of the new property (default: param 1 is an iCalProp with everything - * @param array $parameters The key/value parameter pairs (default: none, or param 1 is an iCalProp with everything) - */ - function AddProperty( $new_property, $value = null, $parameters = null ) { - if ( isset($this->rendered) ) unset($this->rendered); - if ( isset($value) && gettype($new_property) == 'string' ) { - $new_prop = new iCalProp(); - $new_prop->Name($new_property); - $new_prop->Value($value); - if ( $parameters != null ) $new_prop->Parameters($parameters); - dbg_error_log('iCalendar'," Adding new property '%s'", $new_prop->Render() ); - $this->properties[] = $new_prop; - } - else if ( gettype($new_property) ) { - $this->properties[] = $new_property; - } - } - - - /** - * Get all sub-components, or at least get those matching a type - * @return array an array of the sub-components - */ - function &FirstNonTimezone( $type = null ) { - foreach( $this->components AS $k => $v ) { - if ( $v->GetType() != 'VTIMEZONE' ) return $this->components[$k]; - } - $result = false; - return $result; - } - - - /** - * Return true if the person identified by the email address is down as an - * organizer for this meeting. - * @param string $email The e-mail address of the person we're seeking. - * @return boolean true if we found 'em, false if we didn't. - */ - function IsOrganizer( $email ) { - if ( !preg_match( '#^mailto:#', $email ) ) $email = 'mailto:$email'; - $props = $this->GetPropertiesByPath('!VTIMEZONE/ORGANIZER'); - foreach( $props AS $k => $prop ) { - if ( $prop->Value() == $email ) return true; - } - return false; - } - - - /** - * Return true if the person identified by the email address is down as an - * attendee or organizer for this meeting. - * @param string $email The e-mail address of the person we're seeking. - * @return boolean true if we found 'em, false if we didn't. - */ - function IsAttendee( $email ) { - if ( !preg_match( '#^mailto:#', $email ) ) $email = 'mailto:$email'; - if ( $this->IsOrganizer($email) ) return true; /** an organizer is an attendee, as far as we're concerned */ - $props = $this->GetPropertiesByPath('!VTIMEZONE/ATTENDEE'); - foreach( $props AS $k => $prop ) { - if ( $prop->Value() == $email ) return true; - } - return false; - } - - - /** - * Get all sub-components, or at least get those matching a type, or failling to match, - * should the second parameter be set to false. - * - * @param string $type The type to match (default: All) - * @param boolean $normal_match Set to false to invert the match (default: true) - * @return array an array of the sub-components - */ - function GetComponents( $type = null, $normal_match = true ) { - $components = $this->components; - if ( $type != null ) { - foreach( $components AS $k => $v ) { - if ( ($v->GetType() != $type) === $normal_match ) { - unset($components[$k]); - } - } - $components = array_values($components); - } - return $components; - } - - - /** - * Clear all components, or the components matching a particular type - * @param string $type The type of component - omit for all components - */ - function ClearComponents( $type = null ) { - if ( $type != null ) { - // First remove all the existing ones of that type - foreach( $this->components AS $k => $v ) { - if ( $v->GetType() == $type ) { - unset($this->components[$k]); - if ( isset($this->rendered) ) unset($this->rendered); - } - else { - if ( ! $this->components[$k]->ClearComponents($type) ) { - if ( isset($this->rendered) ) unset($this->rendered); - } - } - } - return isset($this->rendered); - } - else { - if ( isset($this->rendered) ) unset($this->rendered); - $this->components = array(); - } - } - - - /** - * Sets some or all sub-components of the component to the supplied new components - * - * @param array of iCalComponent $new_components The new components to replace the existing ones - * @param string $type The type of components to be replaced. Defaults to null, which means all components will be replaced. - */ - function SetComponents( $new_component, $type = null ) { - if ( isset($this->rendered) ) unset($this->rendered); - if ( count($new_component) > 0 ) $this->ClearComponents($type); - foreach( $new_component AS $k => $v ) { - $this->components[] = $v; - } - } - - - /** - * Adds a new subcomponent - * - * @param iCalComponent $new_component The new component to append to the set - */ - function AddComponent( $new_component ) { - if ( is_array($new_component) && count($new_component) == 0 ) return; - if ( isset($this->rendered) ) unset($this->rendered); - if ( is_array($new_component) ) { - foreach( $new_component AS $k => $v ) { - $this->components[] = $v; - } - } - else { - $this->components[] = $new_component; - } - } - - - /** - * Mask components, removing any that are not of the types in the list - * @param array $keep An array of component types to be kept - */ - function MaskComponents( $keep ) { - foreach( $this->components AS $k => $v ) { - if ( ! in_array( $v->GetType(), $keep ) ) { - unset($this->components[$k]); - if ( isset($this->rendered) ) unset($this->rendered); - } - else { - $v->MaskComponents($keep); - } - } - } - - - /** - * Mask properties, removing any that are not in the list - * @param array $keep An array of property names to be kept - * @param array $component_list An array of component types to check within - */ - function MaskProperties( $keep, $component_list=null ) { - foreach( $this->components AS $k => $v ) { - $v->MaskProperties($keep, $component_list); - } - - if ( !isset($component_list) || in_array($this->GetType(),$component_list) ) { - foreach( $this->components AS $k => $v ) { - if ( ! in_array( $v->GetType(), $keep ) ) { - unset($this->components[$k]); - if ( isset($this->rendered) ) unset($this->rendered); - } - } - } - } - - - /** - * Clone this component (and subcomponents) into a confidential version of it. A confidential - * event will be scrubbed of any identifying characteristics other than time/date, repeat, uid - * and a summary which is just a translated 'Busy'. - */ - function CloneConfidential() { - $confidential = clone($this); - $keep_properties = array( 'DTSTAMP', 'DTSTART', 'RRULE', 'DURATION', 'DTEND', 'DUE', 'UID', 'CLASS', 'TRANSP', 'CREATED', 'LAST-MODIFIED' ); - $resource_components = array( 'VEVENT', 'VTODO', 'VJOURNAL' ); - $confidential->MaskComponents(array( 'VTIMEZONE', 'VEVENT', 'VTODO', 'VJOURNAL' )); - $confidential->MaskProperties($keep_properties, $resource_components ); - if ( in_array( $confidential->GetType(), $resource_components ) ) { - $confidential->AddProperty( 'SUMMARY', translate('Busy') ); - } - foreach( $confidential->components AS $k => $v ) { - if ( in_array( $v->GetType(), $resource_components ) ) { - $v->AddProperty( 'SUMMARY', translate('Busy') ); - } - } - - return $confidential; - } - - - /** - * Renders the component, possibly restricted to only the listed properties - */ - function Render( $restricted_properties = null) { - - $unrestricted = (!isset($restricted_properties) || count($restricted_properties) == 0); - - if ( isset($this->rendered) && $unrestricted ) - return $this->rendered; - - $rendered = "BEGIN:$this->type\r\n"; - foreach( $this->properties AS $k => $v ) { - if ( method_exists($v, 'Render') ) { - if ( $unrestricted || isset($restricted_properties[$v]) ) $rendered .= $v->Render() . "\r\n"; - } - } - foreach( $this->components AS $v ) { $rendered .= $v->Render(); } - $rendered .= "END:$this->type\r\n"; - - $rendered = preg_replace('{(?rendered = $rendered; - - return $rendered; - } - - - /** - * Return an array of properties matching the specified path - * - * @return array An array of iCalProp within the tree which match the path given, in the form - * [/]COMPONENT[/...]/PROPERTY in a syntax kind of similar to our poor man's XML queries. We - * also allow COMPONENT and PROPERTY to be !COMPONENT and !PROPERTY for ++fun. - * - * @note At some point post PHP4 this could be re-done with an iterator, which should be more efficient for common use cases. - */ - function GetPropertiesByPath( $path ) { - $properties = array(); - dbg_error_log( 'iCalendar', "GetPropertiesByPath: Querying within '%s' for path '%s'", $this->type, $path ); - if ( !preg_match( '#(/?)(!?)([^/]+)(/?.*)$#', $path, $matches ) ) return $properties; - - $adrift = ($matches[1] == ''); - $normal = ($matches[2] == ''); - $ourtest = $matches[3]; - $therest = $matches[4]; - dbg_error_log( 'iCalendar', "GetPropertiesByPath: Matches: %s -- %s -- %s -- %s\n", $matches[1], $matches[2], $matches[3], $matches[4] ); - if ( $ourtest == '*' || (($ourtest == $this->type) === $normal) && $therest != '' ) { - if ( preg_match( '#^/(!?)([^/]+)$#', $therest, $matches ) ) { - $normmatch = ($matches[1] ==''); - $proptest = $matches[2]; - foreach( $this->properties AS $k => $v ) { - if ( $proptest == '*' || (($v->Name() == $proptest) === $normmatch ) ) { - $properties[] = $v; - } - } - } - else { - /** - * There is more to the path, so we recurse into that sub-part - */ - foreach( $this->components AS $k => $v ) { - $properties = array_merge( $properties, $v->GetPropertiesByPath($therest) ); - } - } - } - - if ( $adrift ) { - /** - * Our input $path was not rooted, so we recurse further - */ - foreach( $this->components AS $k => $v ) { - $properties = array_merge( $properties, $v->GetPropertiesByPath($path) ); - } - } - dbg_error_log('iCalendar', "GetPropertiesByPath: Found %d within '%s' for path '%s'\n", count($properties), $this->type, $path ); - return $properties; - } - -} - -/** -************************************************************************************ -* Everything below here is deprecated and should be avoided in favour -* of using, improving and enhancing the more sensible structures above. -************************************************************************************ -*/ - -/** -* A Class for handling Events on a calendar (DEPRECATED) -* -* @package awl -*/ -class iCalendar { // DEPRECATED - /**#@+ - * @access private - */ - - /** - * The component-ised version of the iCalendar - * @var component iCalComponent - */ - var $component; - - /** - * An array of arbitrary properties, containing arbitrary arrays of arbitrary properties - * @var properties array - */ - var $properties; - - /** - * An array of the lines of this iCalendar resource - * @var lines array - */ - var $lines; - - /** - * The typical location name for the standard timezone such as "Pacific/Auckland" - * @var tz_locn string - */ - var $tz_locn; - - /** - * The type of iCalendar data VEVENT/VTODO/VJOURNAL - * @var type string - */ - var $type; - - /**#@-*/ - - /** - * @DEPRECATED: This class will be removed soon. - * The constructor takes an array of args. If there is an element called 'icalendar' - * then that will be parsed into the iCalendar object. Otherwise the array elements - * are converted into properties of the iCalendar object directly. - */ - function iCalendar( $args ) { - global $c; - - deprecated('iCalendar::iCalendar'); - $this->tz_locn = ""; - if ( !isset($args) || !(is_array($args) || is_object($args)) ) return; - if ( is_object($args) ) { - settype($args,'array'); - } - - $this->component = new iCalComponent(); - if ( isset($args['icalendar']) ) { - $this->component->ParseFrom($args['icalendar']); - $this->lines = preg_split('/\r?\n/', $args['icalendar'] ); - $this->SaveTimeZones(); - $first =& $this->component->FirstNonTimezone(); - if ( $first ) { - $this->type = $first->GetType(); - $this->properties = $first->GetProperties(); - } - else { - $this->properties = array(); - } - $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!'); - return; - } - - if ( isset($args['type'] ) ) { - $this->type = $args['type']; - unset( $args['type'] ); - } - else { - $this->type = 'VEVENT'; // Default to event - } - $this->component->SetType('VCALENDAR'); - $this->component->SetProperties( - array( - new iCalProp('PRODID:-//davical.org//NONSGML AWL Calendar//EN'), - new iCalProp('VERSION:2.0'), - new iCalProp('CALSCALE:GREGORIAN') - ) - ); - $first = new iCalComponent(); - $first->SetType($this->type); - $this->properties = array(); - - foreach( $args AS $k => $v ) { - dbg_error_log( 'iCalendar', ":Initialise: %s to >>>%s<<<", $k, $v ); - $property = new iCalProp(); - $property->Name($k); - $property->Value($v); - $this->properties[] = $property; - } - $first->SetProperties($this->properties); - $this->component->SetComponents( array($first) ); - - $this->properties['VCALENDAR'] = array('***ERROR*** This class is being referenced in an unsupported way!'); - - /** - * @todo Need to handle timezones!!! - */ - if ( $this->tz_locn == "" ) { - $this->tz_locn = $this->Get("tzid"); - if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) { - $this->tz_locn = $c->local_tzid; - } - } - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Save any timezones by TZID in the PostgreSQL database for future re-use. - */ - function SaveTimeZones() { - global $c; - - deprecated('iCalendar::SaveTimeZones'); - $this->tzid_list = array_keys($this->component->CollectParameterValues('TZID')); - if ( ! isset($this->tzid) && count($this->tzid_list) > 0 ) { - dbg_error_log( 'iCalendar', "::TZID_List[0] = '%s', count=%d", $this->tzid_list[0], count($this->tzid_list) ); - $this->tzid = $this->tzid_list[0]; - } - - $timezones = $this->component->GetComponents('VTIMEZONE'); - if ( $timezones === false || count($timezones) == 0 ) return; - $this->vtimezone = $timezones[0]->Render(); // Backward compatibility - - $tzid = $this->Get('TZID'); - if ( isset($c->save_time_zone_defs) && $c->save_time_zone_defs ) { - foreach( $timezones AS $k => $tz ) { - $tzid = $tz->GetPValue('TZID'); - - $qry = new AwlQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $tzid ); - if ( $qry->Exec('iCalendar') && $qry->rows() == 1 ) { - $row = $qry->Fetch(); - if ( !isset($first_tzid) ) $first_tzid = $row->tz_locn; - continue; - } - - if ( $tzid != "" && $qry->rows() == 0 ) { - - $tzname = $tz->GetPValue('X-LIC-LOCATION'); - if ( !isset($tzname) ) $tzname = olson_from_tzstring($tzid); - - $qry2 = new AwlQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );", - $tzid, $tzname, $tz->Render() ); - $qry2->Exec('iCalendar'); - } - } - } - if ( ! isset($this->tzid) && isset($first_tzid) ) $this->tzid = $first_tzid; - - if ( (!isset($this->tz_locn) || $this->tz_locn == '') && isset($first_tzid) && $first_tzid != '' ) { - $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i','$2', $first_tzid ); - if ( preg_match( '#\S+/\S+#', $tzname) ) { - $this->tz_locn = $tzname; - } - dbg_error_log( 'iCalendar', " TZCrap1: TZID '%s', Location '%s', Perhaps: %s", $tzid, $this->tz_locn, $tzname ); - } - - if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) { - $this->tz_locn = $c->local_tzid; - } - if ( ! isset($this->tzid) && isset($this->tz_locn) ) $this->tzid = $this->tz_locn; - } - - - /** - * An array of property names that we should always want when rendering an iCalendar - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function DefaultPropertyList() { - dbg_error_log( "LOG", " iCalendar: Call to deprecated method '%s'", 'DefaultPropertyList' ); - return array( "UID" => 1, "DTSTAMP" => 1, "DTSTART" => 1, "DURATION" => 1, - "LAST-MODIFIED" => 1,"CLASS" => 1, "TRANSP" => 1, "SEQUENCE" => 1, - "DUE" => 1, "SUMMARY" => 1, "RRULE" => 1 ); - } - - /** - * A function to extract the contents of a BEGIN:SOMETHING to END:SOMETHING (perhaps multiply) - * and return just that bit (or, of course, those bits :-) - * - * @var string The type of thing(s) we want returned. - * @var integer The number of SOMETHINGS we want to get. - * - * @return string A string from BEGIN:SOMETHING to END:SOMETHING, possibly multiple of these - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function JustThisBitPlease( $type, $count=1 ) { - deprecated('iCalendar::JustThisBitPlease' ); - $answer = ""; - $intags = false; - $start = "BEGIN:$type"; - $finish = "END:$type"; - dbg_error_log( 'iCalendar', ":JTBP: Looking for %d subsets of type %s", $count, $type ); - reset($this->lines); - foreach( $this->lines AS $k => $v ) { - if ( !$intags && $v == $start ) { - $answer .= $v . "\n"; - $intags = true; - } - else if ( $intags && $v == $finish ) { - $answer .= $v . "\n"; - $intags = false; - } - else if ( $intags ) { - $answer .= $v . "\n"; - } - } - return $answer; - } - - - /** - * Function to parse lines from BEGIN:SOMETHING to END:SOMETHING into a nested array structure - * - * @var string The "SOMETHING" from the BEGIN:SOMETHING line we just met - * @return arrayref An array of the things we found between (excluding) the BEGIN & END, some of which might be sub-arrays - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function &ParseSomeLines( $type ) { - deprecated('iCalendar::ParseSomeLines' ); - $props = array(); - $properties =& $props; - while( isset($this->lines[$this->_current_parse_line]) ) { - $i = $this->_current_parse_line++; - $line =& $this->lines[$i]; - dbg_error_log( 'iCalendar', ":Parse:%s LINE %03d: >>>%s<<<", $type, $i, $line ); - if ( $this->parsing_vtimezone ) { - $this->vtimezone .= $line."\n"; - } - if ( preg_match( '/^(BEGIN|END):([^:]+)$/', $line, $matches ) ) { - if ( $matches[1] == 'END' && $matches[2] == $type ) { - if ( $type == 'VTIMEZONE' ) { - $this->parsing_vtimezone = false; - } - return $properties; - } - else if( $matches[1] == 'END' ) { - dbg_error_log("ERROR"," iCalendar: parse error: Unexpected END:%s when we were looking for END:%s", $matches[2], $type ); - return $properties; - } - else if( $matches[1] == 'BEGIN' ) { - $subtype = $matches[2]; - if ( $subtype == 'VTIMEZONE' ) { - $this->parsing_vtimezone = true; - $this->vtimezone = $line."\n"; - } - if ( !isset($properties['INSIDE']) ) $properties['INSIDE'] = array(); - $properties['INSIDE'][] = $subtype; - if ( !isset($properties[$subtype]) ) $properties[$subtype] = array(); - $properties[$subtype][] = $this->ParseSomeLines($subtype); - } - } - else { - // Parse the property - @list( $property, $value ) = explode(':', $line, 2 ); - if ( strpos( $property, ';' ) > 0 ) { - $parameterlist = explode(';', $property ); - $property = array_shift($parameterlist); - foreach( $parameterlist AS $pk => $pv ) { - if ( $pv == "VALUE=DATE" ) { - $value .= 'T000000'; - } - elseif ( preg_match('/^([^;:=]+)=([^;:=]+)$/', $pv, $matches) ) { - switch( $matches[1] ) { - case 'TZID': $properties['TZID'] = $matches[2]; break; - default: - dbg_error_log( 'iCalendar', " FYI: Ignoring Resource '%s', Property '%s', Parameter '%s', Value '%s'", $type, $property, $matches[1], $matches[2] ); - } - } - } - } - if ( $this->parsing_vtimezone && (!isset($this->tz_locn) || $this->tz_locn == "") && $property == 'X-LIC-LOCATION' ) { - $this->tz_locn = $value; - } - $properties[strtoupper($property)] = $this->RFC2445ContentUnescape($value); - } - } - return $properties; - } - - - /** - * Build the iCalendar object from a text string which is a single iCalendar resource - * - * @var string The RFC2445 iCalendar resource to be parsed - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function BuildFromText( $icalendar ) { - deprecated('iCalendar::BuildFromText' ); - /** - * This unescapes the (CRLF + linear space) wrapping specified in RFC2445. According - * to RFC2445 we should always end with CRLF but the CalDAV spec says that normalising - * XML parsers often muck with it and may remove the CR. - */ - $icalendar = preg_replace('/\r?\n[ \t]/', '', $icalendar ); - - $this->lines = preg_split('/\r?\n/', $icalendar ); - - $this->_current_parse_line = 0; - $this->properties = $this->ParseSomeLines(''); - - /** - * Our 'type' is the type of non-timezone inside a VCALENDAR - */ - if ( isset($this->properties['VCALENDAR'][0]['INSIDE']) ) { - foreach ( $this->properties['VCALENDAR'][0]['INSIDE'] AS $k => $v ) { - if ( $v == 'VTIMEZONE' ) continue; - $this->type = $v; - break; - } - } - - } - - - /** - * Returns a content string with the RFC2445 escaping removed - * - * @param string $escaped The incoming string to be escaped. - * @return string The string with RFC2445 content escaping removed. - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function RFC2445ContentUnescape( $escaped ) { - deprecated( 'RFC2445ContentUnescape' ); - $unescaped = str_replace( '\\n', "\n", $escaped); - $unescaped = str_replace( '\\N', "\n", $unescaped); - $unescaped = preg_replace( "/\\\\([,;:\"\\\\])/", '$1', $unescaped); - return $unescaped; - } - - - - /** - * Do what must be done with time zones from on file. Attempt to turn - * them into something that PostgreSQL can understand... - * - * @DEPRECATED: This class will be removed soon. - * @todo Remove this function. - */ - function DealWithTimeZones() { - global $c; - - deprecated('iCalendar::DealWithTimeZones' ); - $tzid = $this->Get('TZID'); - if ( isset($c->save_time_zone_defs) && $c->save_time_zone_defs ) { - $qry = new AwlQuery( "SELECT tz_locn FROM time_zone WHERE tz_id = ?;", $tzid ); - if ( $qry->Exec('iCalendar') && $qry->rows() == 1 ) { - $row = $qry->Fetch(); - $this->tz_locn = $row->tz_locn; - } - dbg_error_log( 'iCalendar', " TZCrap2: TZID '%s', DB Rows=%d, Location '%s'", $tzid, $qry->rows(), $this->tz_locn ); - } - - if ( (!isset($this->tz_locn) || $this->tz_locn == '') && $tzid != '' ) { - /** - * In case there was no X-LIC-LOCATION defined, let's hope there is something in the TZID - * that we can use. We are looking for a string like "Pacific/Auckland" if possible. - */ - $tzname = preg_replace('#^(.*[^a-z])?([a-z]+/[a-z]+)$#i','$1',$tzid ); - /** - * Unfortunately this kind of thing will never work well :-( - * - if ( strstr( $tzname, ' ' ) ) { - $words = preg_split('/\s/', $tzname ); - $tzabbr = ''; - foreach( $words AS $i => $word ) { - $tzabbr .= substr( $word, 0, 1); - } - $this->tz_locn = $tzabbr; - } - */ - if ( preg_match( '#\S+/\S+#', $tzname) ) { - $this->tz_locn = $tzname; - } - dbg_error_log( 'iCalendar', " TZCrap3: TZID '%s', Location '%s', Perhaps: %s", $tzid, $this->tz_locn, $tzname ); - } - - if ( $tzid != '' && isset($c->save_time_zone_defs) && $c->save_time_zone_defs && $qry->rows() != 1 && isset($this->vtimezone) && $this->vtimezone != "" ) { - $qry2 = new AwlQuery( "INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES( ?, ?, ? );", - $tzid, $this->tz_locn, $this->vtimezone ); - $qry2->Exec('iCalendar'); - } - - if ( (!isset($this->tz_locn) || $this->tz_locn == "") && isset($c->local_tzid) ) { - $this->tz_locn = $c->local_tzid; - } - } - - - /** - * Get the value of a property in the first non-VTIMEZONE - * @DEPRECATED: This class will be removed soon. - */ - function Get( $key ) { - deprecated('iCalendar::Get' ); - if ( strtoupper($key) == 'TZID' ) { - // backward compatibility hack - dbg_error_log( 'iCalendar', " Get(TZID): TZID '%s', Location '%s'", (isset($this->tzid)?$this->tzid:"[not set]"), $this->tz_locn ); - if ( isset($this->tzid) ) return $this->tzid; - return $this->tz_locn; - } - /** - * The property we work on is the first non-VTIMEZONE we find. - */ - $component =& $this->component->FirstNonTimezone(); - if ( $component === false ) return null; - return $component->GetPValue(strtoupper($key)); - } - - - /** - * Set the value of a property - * @DEPRECATED: This class will be removed soon. - */ - function Set( $key, $value ) { - deprecated('iCalendar::Set' ); - if ( $value == "" ) return; - $key = strtoupper($key); - $property = new iCalProp(); - $property->Name($key); - $property->Value($value); - if (isset($this->component->rendered) ) unset( $this->component->rendered ); - $component =& $this->component->FirstNonTimezone(); - $component->SetProperties( array($property), $key); - return $this->Get($key); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Add a new property/value, regardless of whether it exists already - * - * @param string $key The property key - * @param string $value The property value - * @param string $parameters Any parameters to set for the property, as an array of key/value pairs - */ - function Add( $key, $value, $parameters = null ) { - deprecated('iCalendar::Add' ); - if ( $value == "" ) return; - $key = strtoupper($key); - $property = new iCalProp(); - $property->Name($key); - $property->Value($value); - if ( isset($parameters) && is_array($parameters) ) { - $property->parameters = $parameters; - } - $component =& $this->component->FirstNonTimezone(); - $component->AddProperty($property); - if (isset($this->component->rendered) ) unset( $this->component->rendered ); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Get all sub-components, or at least get those matching a type, or failling to match, - * should the second parameter be set to false. - * - * @param string $type The type to match (default: All) - * @param boolean $normal_match Set to false to invert the match (default: true) - * @return array an array of the sub-components - */ - function GetComponents( $type = null, $normal_match = true ) { - deprecated('iCalendar::GetComponents' ); - return $this->component->GetComponents($type,$normal_match); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Clear all components, or the components matching a particular type - * @param string $type The type of component - omit for all components - */ - function ClearComponents( $type = null ) { - deprecated('iCalendar::ClearComponents' ); - $this->component->ClearComponents($type); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Sets some or all sub-components of the component to the supplied new components - * - * @param array of iCalComponent $new_components The new components to replace the existing ones - * @param string $type The type of components to be replaced. Defaults to null, which means all components will be replaced. - */ - function SetComponents( $new_component, $type = null ) { - deprecated('iCalendar::SetComponents' ); - $this->component->SetComponents( $new_component, $type ); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Adds a new subcomponent - * - * @param iCalComponent $new_component The new component to append to the set - */ - function AddComponent( $new_component ) { - deprecated('iCalendar::AddComponent' ); - $this->component->AddComponent($new_component); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Mask components, removing any that are not of the types in the list - * @param array $keep An array of component types to be kept - */ - function MaskComponents( $keep ) { - deprecated('iCalendar::MaskComponents' ); - $this->component->MaskComponents($keep); - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Returns a PostgreSQL Date Format string suitable for returning HTTP (RFC2068) dates - * Preferred is "Sun, 06 Nov 1994 08:49:37 GMT" so we do that. - */ - static function HttpDateFormat() { - return "'Dy, DD Mon IYYY HH24:MI:SS \"GMT\"'"; - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Returns a PostgreSQL Date Format string suitable for returning iCal dates - */ - static function SqlDateFormat() { - return "'YYYYMMDD\"T\"HH24MISS'"; - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Returns a PostgreSQL Date Format string suitable for returning dates which - * have been cast to UTC - */ - static function SqlUTCFormat() { - return "'YYYYMMDD\"T\"HH24MISS\"Z\"'"; - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Returns a PostgreSQL Date Format string suitable for returning iCal durations - * - this doesn't work for negative intervals, but events should not have such! - */ - static function SqlDurationFormat() { - return "'\"PT\"HH24\"H\"MI\"M\"'"; - } - - /** - * @DEPRECATED: This class will be removed soon. - * Returns a suitably escaped RFC2445 content string. - * - * @param string $name The incoming name[;param] prefixing the string. - * @param string $value The incoming string to be escaped. - * - * @deprecated This function is deprecated and will be removed eventually. - * @todo Remove this function. - */ - function RFC2445ContentEscape( $name, $value ) { - deprecated('iCalendar::RFC2445ContentEscape' ); - $property = preg_replace( '/[;].*$/', '', $name ); - switch( $property ) { - /** Content escaping does not apply to these properties culled from RFC2445 */ - case 'ATTACH': case 'GEO': case 'PERCENT-COMPLETE': case 'PRIORITY': - case 'COMPLETED': case 'DTEND': case 'DUE': case 'DTSTART': - case 'DURATION': case 'FREEBUSY': case 'TZOFFSETFROM': case 'TZOFFSETTO': - case 'TZURL': case 'ATTENDEE': case 'ORGANIZER': case 'RECURRENCE-ID': - case 'URL': case 'EXDATE': case 'EXRULE': case 'RDATE': - case 'RRULE': case 'REPEAT': case 'TRIGGER': case 'CREATED': - case 'DTSTAMP': case 'LAST-MODIFIED': case 'SEQUENCE': - break; - - /** Content escaping applies by default to other properties */ - default: - $value = str_replace( '\\', '\\\\', $value); - $value = preg_replace( '/\r?\n/', '\\n', $value); - $value = preg_replace( "/([,;:\"])/", '\\\\$1', $value); - } - $result = preg_replace( '/(.{72})/u', '$1'."\r\n ", $name.':'.$value ) ."\r\n"; - return $result; - } - - /** - * @DEPRECATED: This class will be removed soon. - * Return all sub-components of the given type, which are part of the - * component we pass in as an array of lines. - * - * @param array $component The component to be parsed - * @param string $type The type of sub-components to be extracted - * @param int $count The number of sub-components to extract (default: 9999) - * - * @return array The sub-component lines - */ - function ExtractSubComponent( $component, $type, $count=9999 ) { - deprecated('iCalendar::ExtractSubComponent' ); - $answer = array(); - $intags = false; - $start = "BEGIN:$type"; - $finish = "END:$type"; - dbg_error_log( 'iCalendar', ":ExtractSubComponent: Looking for %d subsets of type %s", $count, $type ); - reset($component); - foreach( $component AS $k => $v ) { - if ( !$intags && $v == $start ) { - $answer[] = $v; - $intags = true; - } - else if ( $intags && $v == $finish ) { - $answer[] = $v; - $intags = false; - } - else if ( $intags ) { - $answer[] = $v; - } - } - return $answer; - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Extract a particular property from the provided component. In doing so we - * assume that the content was unescaped when iCalComponent::ParseFrom() - * called iCalComponent::UnwrapComponent(). - * - * @param array $component An array of lines of this component - * @param string $type The type of parameter - * - * @return array An array of iCalProperty objects - */ - function ExtractProperty( $component, $type, $count=9999 ) { - deprecated('iCalendar::ExtractProperty' ); - $answer = array(); - dbg_error_log( 'iCalendar', ":ExtractProperty: Looking for %d properties of type %s", $count, $type ); - reset($component); - foreach( $component AS $k => $v ) { - if ( preg_match( "/$type"."[;:]/i", $v ) ) { - $answer[] = new iCalProp($v); - dbg_error_log( 'iCalendar', ":ExtractProperty: Found property %s", $type ); - if ( --$count < 1 ) return $answer; - } - } - return $answer; - } - - - /** - * @DEPRECATED: This class will be removed soon. - * Applies the filter conditions, possibly recursively, to the value which will be either - * a single property, or an array of lines of the component under test. - * - * @todo Eventually we need to handle all of these possibilities, which will mean writing - * several routines: - * - Get Property from Component - * - Get Parameter from Property - * - Test TimeRange - * For the moment we will leave these, until there is a perceived need. - * - * @param array $filter An array of XMLElement defining the filter(s) - * @param mixed $value Either a string which is the single property, or an array of lines, for the component. - * @return boolean Whether the filter passed / failed. - */ - function ApplyFilter( $filter, $value ) { - deprecated('iCalendar::ApplyFilter' ); - foreach( $filter AS $k => $v ) { - $tag = $v->GetTag(); - $value_type = gettype($value); - $value_defined = (isset($value) && $value_type == 'string') || ($value_type == 'array' && count($value) > 0 ); - if ( $tag == 'urn:ietf:params:xml:ns:caldav:is-not-defined' && $value_defined ) { - dbg_error_log( 'iCalendar', ":ApplyFilter: Value is set ('%s'), want unset, for filter %s", count($value), $tag ); - return false; - } - elseif ( $tag == 'urn:ietf:params:xml:ns:caldav:is-defined' && !$value_defined ) { - dbg_error_log( 'iCalendar', ":ApplyFilter: Want value, but it is not set for filter %s", $tag ); - return false; - } - else { - dbg_error_log( 'iCalendar', ":ApplyFilter: Have values for '%s' filter", $tag ); - switch( $tag ) { - case 'urn:ietf:params:xml:ns:caldav:time-range': - /** todo:: While this is unimplemented here at present, most time-range tests should occur at the SQL level. */ - break; - case 'urn:ietf:params:xml:ns:caldav:text-match': - $search = $v->GetContent(); - // In this case $value will either be a string, or an array of iCalProp objects - // since TEXT-MATCH does not apply to COMPONENT level - only property/parameter - if ( !is_string($value) ) { - if ( is_array($value) ) { - $match = false; - foreach( $value AS $k1 => $v1 ) { - // $v1 MUST be an iCalProp object - if ( $match = $v1->TextMatch($search)) break; - } - } - else { - dbg_error_log( 'iCalendar', ":ApplyFilter: TEXT-MATCH will only work on strings or arrays of iCalProp. %s unsupported", gettype($value) ); - return true; // We return _true_ in this case, so the client sees the item - } - } - else { - $match = (stristr( $value, $search ) !== false); - } - $negate = $v->GetAttribute("negate-condition"); - if ( isset($negate) && strtolower($negate) == "yes" ) $match = !$match; -// dbg_error_log( 'iCalendar', ":ApplyFilter: TEXT-MATCH returning %s", ($match?"yes":"no") ); - return $match; - break; - case 'urn:ietf:params:xml:ns:caldav:comp-filter': - $subfilter = $v->GetContent(); - $component = $this->ExtractSubComponent($value,$v->GetAttribute("name")); - if ( ! $this->ApplyFilter($subfilter,$component) ) return false; - break; - case 'urn:ietf:params:xml:ns:caldav:prop-filter': - $subfilter = $v->GetContent(); - $properties = $this->ExtractProperty($value,$v->GetAttribute("name")); - if ( ! $this->ApplyFilter($subfilter,$properties) ) return false; - break; - case 'urn:ietf:params:xml:ns:caldav:param-filter': - $subfilter = $v->GetContent(); - $parameter = $this->ExtractParameter($value,$v->GetAttribute("NAME")); - if ( ! $this->ApplyFilter($subfilter,$parameter) ) return false; - break; - } - } - } - return true; - } - - /** - * @DEPRECATED: This class will be removed soon. - * Test a PROP-FILTER or COMP-FILTER and return a true/false - * COMP-FILTER (is-defined | is-not-defined | (time-range?, prop-filter*, comp-filter*)) - * PROP-FILTER (is-defined | is-not-defined | ((time-range | text-match)?, param-filter*)) - * - * @param array $filter An array of XMLElement defining the filter - * - * @return boolean Whether or not this iCalendar passes the test - */ - function TestFilter( $filters ) { - deprecated('iCalendar::TestFilter' ); - -// dbg_error_log('iCalendar', ':TestFilter we have %d filters to test', count($filters) ); - foreach( $filters AS $k => $v ) { - $tag = $v->GetTag(); -// dbg_error_log('iCalendar', ':TestFilter working on tag "%s" %s"', $k, $tag ); - $name = $v->GetAttribute("name"); - $filter = $v->GetContent(); - if ( $tag == "urn:ietf:params:xml:ns:caldav:prop-filter" ) { - $value = $this->ExtractProperty($this->lines,$name); - } - else { - $value = $this->ExtractSubComponent($this->lines,$v->GetAttribute("name")); - } - if ( count($value) == 0 ) unset($value); - if ( ! $this->ApplyFilter($filter,$value) ) return false; - } - return true; - } - - /** - * @DEPRECATED: This class will be removed soon. - * Returns the header we always use at the start of our iCalendar resources - * - * @todo Remove this function. - */ - static function iCalHeader() { - deprecated('iCalendar::iCalHeader' ); - return <<component->Render(); - } - else { - $components = $this->component->GetComponents($type); - $rendered = ""; - foreach( $components AS $k => $v ) { - $rendered .= $v->Render($restrict_properties); - } - return $rendered; - } - } - -} diff --git a/sources/include/mimeDecode.php b/sources/include/mimeDecode.php deleted file mode 100644 index 6b11cb2..0000000 --- a/sources/include/mimeDecode.php +++ /dev/null @@ -1,1217 +0,0 @@ - - * Copyright (c) 2003-2006, PEAR - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - Neither the name of the authors, nor the names of its contributors - * may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - * - * @category Mail - * @package Mail_Mime - * @author Richard Heyes - * @author George Schlossnagle - * @author Cipriano Groenendal - * @author Sean Coates - * @copyright 2003-2006 PEAR - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version CVS: $Id: mimeDecode.php 335147 2014-10-27 08:41:39Z alan_k $ - * @link http://pear.php.net/package/Mail_mime - */ - - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * implemented automated decoding of strings from mail charset - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail_mimeDecode-1.5.5.tgz - * - * used "old" method of checking if called statically, as this is deprecated between php 5.0.0 and 5.3.0 - * (isStatic of decode() around line 215) - */ - -/** - * require PEAR - * - * This package depends on PEAR to raise errors. - */ -//require_once 'PEAR.php'; - -/** - * The Mail_mimeDecode class is used to decode mail/mime messages - * - * This class will parse a raw mime email and return the structure. - * Returned structure is similar to that returned by imap_fetchstructure(). - * - * +----------------------------- IMPORTANT ------------------------------+ - * | Usage of this class compared to native php extensions such as | - * | mailparse or imap, is slow and may be feature deficient. If available| - * | you are STRONGLY recommended to use the php extensions. | - * +----------------------------------------------------------------------+ - * - * @category Mail - * @package Mail_Mime - * @author Richard Heyes - * @author George Schlossnagle - * @author Cipriano Groenendal - * @author Sean Coates - * @copyright 2003-2006 PEAR - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/Mail_mime - */ -class Mail_mimeDecode -{ - /** - * The raw email to decode - * - * @var string - * @access private - */ - var $_input; - - /** - * The header part of the input - * - * @var string - * @access private - */ - var $_header; - - /** - * The body part of the input - * - * @var string - * @access private - */ - var $_body; - - /** - * If an error occurs, this is used to store the message - * - * @var string - * @access private - */ - var $_error; - - /** - * Flag to determine whether to include bodies in the - * returned object. - * - * @var boolean - * @access private - */ - var $_include_bodies; - - /** - * Flag to determine whether to decode bodies - * - * @var boolean - * @access private - */ - var $_decode_bodies; - - /** - * Flag to determine whether to decode headers - * - * @var boolean - * @access private - */ - var $_decode_headers; - - - /** - * Flag to determine whether to include attached messages - * as body in the returned object. Depends on $_include_bodies - * - * @var boolean - * @access private - */ - var $_rfc822_bodies; - - /** - * Constructor. - * - * Sets up the object, initialise the variables, and splits and - * stores the header and body of the input. - * - * @param string The input to decode - * @access public - */ - function Mail_mimeDecode($input) - { - list($header, $body) = $this->_splitBodyHeader($input); - - $this->_input = $input; - $this->_header = $header; - $this->_body = $body; - $this->_decode_bodies = false; - $this->_include_bodies = true; - $this->_rfc822_bodies = false; - } - - /** - * Begins the decoding process. If called statically - * it will create an object and call the decode() method - * of it. - * - * @param array An array of various parameters that determine - * various things: - * include_bodies - Whether to include the body in the returned - * object. - * decode_bodies - Whether to decode the bodies - * of the parts. (Transfer encoding) - * decode_headers - Whether to decode headers - * - * input - If called statically, this will be treated - * as the input - * charset - convert all data to this charset - * @return object Decoded results - * @access public - */ - function decode($params = null) - { - // determine if this method has been called statically - $isStatic = empty($this) || !is_a($this, __CLASS__); - - // Have we been called statically? - // If so, create an object and pass details to that. - if ($isStatic AND isset($params['input'])) { - - $obj = new Mail_mimeDecode($params['input']); - $structure = $obj->decode($params); - - // Called statically but no input - } elseif ($isStatic) { - return $this->raiseError('Called statically and no input given'); - - // Called via an object - } else { - $this->_include_bodies = isset($params['include_bodies']) ? - $params['include_bodies'] : false; - $this->_decode_bodies = isset($params['decode_bodies']) ? - $params['decode_bodies'] : false; - $this->_decode_headers = isset($params['decode_headers']) ? - $params['decode_headers'] : false; - $this->_rfc822_bodies = isset($params['rfc_822bodies']) ? - $params['rfc_822bodies'] : false; - $this->_charset = isset($params['charset']) ? - strtolower($params['charset']) : 'utf-8'; - - - if (is_string($this->_decode_headers) && !function_exists('iconv')) { - $this->raiseError('header decode conversion requested, however iconv is missing'); - } - - $structure = $this->_decode($this->_header, $this->_body); - if ($structure === false) { - $structure = $this->raiseError($this->_error); - } - } - - return $structure; - } - - /** - * Performs the decoding. Decodes the body string passed to it - * If it finds certain content-types it will call itself in a - * recursive fashion - * - * @param string Header section - * @param string Body section - * @return object Results of decoding process - * @access private - */ - function _decode($headers, $body, $default_ctype = 'text/plain') - { - $return = new stdClass; - $return->headers = array(); - $headers = $this->_parseHeaders($headers); - - foreach ($headers as $value) { - $value['value'] = $this->_decodeHeader($value['value']); - if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { - $return->headers[strtolower($value['name'])] = array($return->headers[strtolower($value['name'])]); - $return->headers[strtolower($value['name'])][] = $value['value']; - - } elseif (isset($return->headers[strtolower($value['name'])])) { - $return->headers[strtolower($value['name'])][] = $value['value']; - - } else { - $return->headers[strtolower($value['name'])] = $value['value']; - } - } - - - foreach ($headers as $key => $value) { - $headers[$key]['name'] = strtolower($headers[$key]['name']); - switch ($headers[$key]['name']) { - - case 'content-type': - $content_type = $this->_parseHeaderValue($headers[$key]['value']); - - if (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)\; name=\"([0-9a-z+.-]+)/i', $headers[$key]['value'], $regs)) { - $return->ctype_primary = $regs[1]; - $return->ctype_secondary = $regs[2]; - $return->filename = $regs[3]; - } - elseif (preg_match('/([0-9a-z+.-]+)\/([0-9a-z+.-]+)/i', $content_type['value'], $regs)) { - $return->ctype_primary = $regs[1]; - $return->ctype_secondary = $regs[2]; - } - - if (isset($content_type['other'])) { - foreach($content_type['other'] as $p_name => $p_value) { - $return->ctype_parameters[$p_name] = $p_value; - } - } - break; - - case 'content-disposition': - $content_disposition = $this->_parseHeaderValue($headers[$key]['value']); - $return->disposition = $content_disposition['value']; - if (isset($content_disposition['other'])) { - foreach($content_disposition['other'] as $p_name => $p_value) { - $return->d_parameters[$p_name] = $p_value; - } - } - break; - - case 'content-transfer-encoding': - $content_transfer_encoding = $this->_parseHeaderValue($headers[$key]['value']); - break; - } - } - - if (isset($content_type)) { - switch (strtolower($content_type['value'])) { - case 'text/plain': - $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; - $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; - $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset, true) : $body) : null; - break; - - case 'text/html': - $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; - $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; - $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset, true) : $body) : null; - break; - - case 'multipart/signed': // PGP - case 'multipart/encrypted': // #190 encrypted parts will be treated as normal ones - case 'multipart/parallel': - case 'multipart/appledouble': // Appledouble mail - case 'multipart/report': // RFC1892 - case 'multipart/digest': - case 'multipart/alternative': - case 'multipart/related': - case 'multipart/relative': //#20431 - android - case 'multipart/mixed': - case 'application/vnd.wap.multipart.related': - if(!isset($content_type['other']['boundary'])){ - $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; - return false; - } - - $default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain'; - - $parts = $this->_boundarySplit($body, $content_type['other']['boundary']); - for ($i = 0; $i < count($parts); $i++) { - list($part_header, $part_body) = $this->_splitBodyHeader($parts[$i]); - $part = $this->_decode($part_header, $part_body, $default_ctype); - if($part === false) - $part = $this->raiseError($this->_error); - $return->parts[] = $part; - } - break; - - case 'message/rfc822': - case 'message/delivery-status': // #bug #18693 - if ($this->_rfc822_bodies) { - $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; - $charset = isset($return->ctype_parameters['charset']) ? $return->ctype_parameters['charset'] : $this->_charset; - $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding, $charset, false) : $body); - } - - $obj = new Mail_mimeDecode($body); - $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, - 'decode_bodies' => $this->_decode_bodies, - 'decode_headers' => $this->_decode_headers)); - unset($obj); - - // #213, KD 2015-06-29 - Always inline them because there is no "type" to them (they're text) - $return->disposition = 'inline'; - break; - - // #190, KD 2015-06-09 - Add type for S/MIME Encrypted messages; these must have the filename set explicitly (it won't work otherwise) - //and then falls through for the rest on purpose. - case 'application/x-pkcs7-mime': - case 'application/pkcs7-mime': - if (!isset($content_transfer_encoding['value'])) { - $content_transfer_encoding['value'] = 'base64'; - } - // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted - $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) ) ? $return->ctype_parameters['charset'] : ''; - $part->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset, false) : $body); - $ctype = explode('/', strtolower($content_type['value'])); - $part->ctype_parameters['name'] = 'smime.p7m'; - $part->ctype_primary = $ctype[0]; - $part->ctype_secondary = $ctype[1]; - $part->d_parameters['size'] = strlen($part->body); - $return->parts[] = $part; - // Fall through intentionally - - default: - if(!isset($content_transfer_encoding['value'])) - $content_transfer_encoding['value'] = '7bit'; - // if there is no explicit charset, then don't try to convert to default charset, and make sure that only text mimetypes are converted - $charset = (isset($return->ctype_parameters['charset']) && ((isset($return->ctype_primary) && $return->ctype_primary == 'text') || !isset($return->ctype_primary)) )? $return->ctype_parameters['charset']: ''; - $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $content_transfer_encoding['value'], $charset, false) : $body) : null; - break; - } - - } else { - $ctype = explode('/', $default_ctype); - $return->ctype_primary = $ctype[0]; - $return->ctype_secondary = $ctype[1]; - $this->_include_bodies ? $return->body = ($this->_decode_bodies ? $this->_decodeBody($body) : $body) : null; - } - - return $return; - } - - /** - * Given the output of the above function, this will return an - * array of references to the parts, indexed by mime number. - * - * @param object $structure The structure to go through - * @param string $mime_number Internal use only. - * @return array Mime numbers - */ - function &getMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '') - { - $return = array(); - if (!empty($structure->parts)) { - if ($mime_number != '') { - $structure->mime_id = $prepend . $mime_number; - $return[$prepend . $mime_number] = &$structure; - } - for ($i = 0; $i < count($structure->parts); $i++) { - - - if (!empty($structure->headers['content-type']) AND substr(strtolower($structure->headers['content-type']), 0, 8) == 'message/') { - $prepend = $prepend . $mime_number . '.'; - $_mime_number = ''; - } else { - $_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1)); - } - - $arr = &Mail_mimeDecode::getMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend); - foreach ($arr as $key => $val) { - $no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key]; - } - } - } else { - if ($mime_number == '') { - $mime_number = '1'; - } - $structure->mime_id = $prepend . $mime_number; - $no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure; - } - - return $return; - } - - /** - * Given a string containing a header and body - * section, this function will split them (at the first - * blank line) and return them. - * - * @param string Input to split apart - * @return array Contains header and body section - * @access private - */ - function _splitBodyHeader($input) - { - if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $input, $match)) { - return array($match[1], $match[2]); - } - // bug #17325 - empty bodies are allowed. - we just check that at least one line - // of headers exist.. - if (count(explode("\n",$input))) { - return array($input, ''); - } - $this->_error = 'Could not split header and body'; - return false; - } - - /** - * Parse headers given in $input and return - * as assoc array. - * - * @param string Headers to parse - * @return array Contains parsed headers - * @access private - */ - function _parseHeaders($input) - { - if ($input !== '') { - // Unfold the input - $input = preg_replace("/\r?\n/", "\r\n", $input); - //#7065 - wrapping.. with encoded stuff.. - probably not needed, - // wrapping space should only get removed if the trailing item on previous line is a - // encoded character - $input = preg_replace("/=\r\n(\t| )+/", '=', $input); - $input = preg_replace("/\r\n(\t| )+/", ' ', $input); - - $headers = explode("\r\n", trim($input)); - $got_start = false; - foreach ($headers as $value) { - if (!$got_start) { - // munge headers for mbox style from - if ($value[0] == '>') { - $value = substring($value, 1); // remove mbox > - } - if (substr($value,0,5) == 'From ') { - $value = 'Return-Path: ' . substr($value, 5); - } else { - $got_start = true; - } - } - - $hdr_name = substr($value, 0, $pos = strpos($value, ':')); - $hdr_value = substr($value, $pos+1); - if($hdr_value[0] == ' ') { - $hdr_value = substr($hdr_value, 1); - } - - $return[] = array( - 'name' => $hdr_name, - 'value' => $hdr_value - ); - } - } else { - $return = array(); - } - - return $return; - } - - /** - * Function to parse a header value, - * extract first part, and any secondary - * parts (after ;) This function is not as - * robust as it could be. Eg. header comments - * in the wrong place will probably break it. - * - * Extra things this can handle - * filename*0=...... - * filename*1=...... - * - * This is where lines are broken in, and need merging. - * - * filename*0*=ENC'lang'urlencoded data. - * filename*1*=ENC'lang'urlencoded data. - * - * - * - * @param string Header value to parse - * @return array Contains parsed result - * @access private - */ - function _parseHeaderValue($input) - { - if (($pos = strpos($input, ';')) === false) { - $input = $this->_decodeHeader($input); - $return['value'] = trim($input); - return $return; - } - - - - $value = substr($input, 0, $pos); - $value = $this->_decodeHeader($value); - $return['value'] = trim($value); - $input = trim(substr($input, $pos+1)); - - if (!strlen($input) > 0) { - return $return; - } - // at this point input contains xxxx=".....";zzzz="...." - // since we are dealing with quoted strings, we need to handle this properly.. - $i = 0; - $l = strlen($input); - $key = ''; - $val = false; // our string - including quotes.. - $q = false; // in quote.. - $lq = ''; // last quote.. - - while ($i < $l) { - - $c = $input[$i]; - //var_dump(array('i'=>$i,'c'=>$c,'q'=>$q, 'lq'=>$lq, 'key'=>$key, 'val' =>$val)); - - $escaped = false; - if ($c == '\\') { - $i++; - if ($i == $l-1) { // end of string. - break; - } - $escaped = true; - $c = $input[$i]; - } - - - // state - in key.. - if ($val === false) { - if (!$escaped && $c == '=') { - $val = ''; - $key = trim($key); - $i++; - continue; - } - if (!$escaped && $c == ';') { - if ($key) { // a key without a value.. - $key= trim($key); - $return['other'][$key] = ''; - } - $key = ''; - } - $key .= $c; - $i++; - continue; - } - - // state - in value.. (as $val is set..) - - if ($q === false) { - // not in quote yet. - if ((!strlen($val) || $lq !== false) && $c == ' ' || $c == "\t") { - $i++; - continue; // skip leading spaces after '=' or after '"' - } - - // do not de-quote 'xxx*= itesm.. - $key_is_trans = $key[strlen($key)-1] == '*'; - - if (!$key_is_trans && !$escaped && ($c == '"' || $c == "'")) { - // start quoted area.. - $q = $c; - // in theory should not happen raw text in value part.. - // but we will handle it as a merged part of the string.. - $val = !strlen(trim($val)) ? '' : trim($val); - $i++; - continue; - } - // got end.... - if (!$escaped && $c == ';') { - - $return['other'][$key] = trim($val); - $val = false; - $key = ''; - $lq = false; - $i++; - continue; - } - - $val .= $c; - $i++; - continue; - } - - // state - in quote.. - if (!$escaped && $c == $q) { // potential exit state.. - - // end of quoted string.. - $lq = $q; - $q = false; - $i++; - continue; - } - - // normal char inside of quoted string.. - $val.= $c; - $i++; - } - - // do we have anything left.. - if (strlen(trim($key)) || $val !== false) { - - $val = trim($val); - - $return['other'][$key] = $val; - } - - - $clean_others = array(); - // merge added values. eg. *1[*] - foreach($return['other'] as $key =>$val) { - if (preg_match('/\*[0-9]+\**$/', $key)) { - $key = preg_replace('/(.*)\*[0-9]+(\**)$/', '\1\2', $key); - if (isset($clean_others[$key])) { - $clean_others[$key] .= $val; - continue; - } - - } - $clean_others[$key] = $val; - - } - - // handle language translation of '*' ending others. - foreach( $clean_others as $key =>$val) { - if ( $key[strlen($key)-1] != '*') { - $clean_others[strtolower($key)] = $val; - continue; - } - unset($clean_others[$key]); - $key = substr($key,0,-1); - //extended-initial-value := [charset] "'" [language] "'" - // extended-other-values - $match = array(); - $info = preg_match("/^([^']+)'([^']*)'(.*)$/", $val, $match); - - $clean_others[$key] = urldecode($match[3]); - $clean_others[strtolower($key)] = $clean_others[$key]; - $clean_others[strtolower($key).'-charset'] = $match[1]; - $clean_others[strtolower($key).'-language'] = $match[2]; - - - } - - - $return['other'] = $clean_others; - - // decode values. - foreach($return['other'] as $key =>$val) { - $charset = isset($return['other'][$key . '-charset']) ? - $return['other'][$key . '-charset'] : false; - - $return['other'][$key] = $this->_decodeHeader($val, $charset); - } - - return $return; - } - - /** - * This function splits the input based - * on the given boundary - * - * @param string Input to parse - * @return array Contains array of resulting mime parts - * @access private - */ - function _boundarySplit($input, $boundary, $eatline = false) - { - $parts = array(); - - $bs_possible = substr($boundary, 2, -2); - $bs_check = '\"' . $bs_possible . '\"'; - - if ($boundary == $bs_check) { - $boundary = $bs_possible; - } - // eatline is used by multipart/signed. - $tmp = $eatline ? - preg_split("/\r?\n--".preg_quote($boundary, '/')."(|--)\n/", $input) : - preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input); - - $len = count($tmp) -1; - for ($i = 1; $i < $len; $i++) { - if (strlen(trim($tmp[$i]))) { - $parts[] = $tmp[$i]; - } - } - - // add the last part on if it does not end with the 'closing indicator' - if (!empty($tmp[$len]) && strlen(trim($tmp[$len])) && $tmp[$len][0] != '-') { - $parts[] = $tmp[$len]; - } - return $parts; - } - - /** - * Given a header, this function will decode it - * according to RFC2047. Probably not *exactly* - * conformant, but it does pass all the given - * examples (in RFC2047). - * - * @param string Input header value to decode - * @return string Decoded header value - * @access private - */ - function _decodeHeader($input) - { - if (!$this->_decode_headers) { - return $input; - } - // Remove white space between encoded-words - $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); - - $encodedwords = false; - $charset = ''; - - // For each encoded-word... - while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { - $encodedwords = true; - - $encoded = $matches[1]; - $charset = $matches[2]; - $encoding = $matches[3]; - $text = $matches[4]; - - switch (strtolower($encoding)) { - case 'b': - $text = base64_decode($text); - break; - - case 'q': - $text = str_replace('_', ' ', $text); - preg_match_all('/=([a-f0-9]{2})/i', $text, $matches); - foreach ($matches[1] as $value) - $text = str_replace('=' . $value, chr(hexdec($value)), $text); - break; - } - - $text = $this->_autoconvert_encoding($text, $charset); - $input = str_replace($encoded, $text, $input); - } - - if (!$encodedwords) { - $input = $this->_autoconvert_encoding($input, $charset); - } - - return $input; - } - - /** - * Given a body string and an encoding type, - * this function will decode and return it. - * - * @param string Input body to decode - * @param string Encoding type to use. - * @param string Charset - * @param boolean Must try to autodetect the real charset used - * @return string Decoded body - * @access private - */ - function _decodeBody($input, $encoding = '7bit', $charset = '', $detectCharset = true) - { - switch (strtolower($encoding)) { - case 'quoted-printable': - $input = $this->_quotedPrintableDecode($input); - break; - - case 'base64': - $input = base64_decode($input); - break; - - case '7bit': - case '8bit': - default: - break; - } - - return $detectCharset ? $this->_autoconvert_encoding($input, $charset) : $input; - } - - /** - * Error handler dummy for _autoconvert_encoding - * - * @param integer $errno - * @param string $errstr - * @return boolean true - * @access public static - */ - static function _iconv_notice_handler($errno, $errstr) { - return true; - } - - /** - * Autoconvert the text from any encoding. THIS WILL NEVER WORK 100%. - * Will ignore the E_NOTICE for iconv when detecting ilegal charsets - * - * @param string $input Input string to convert - * @param string $supposed_encoding Encoding that the text is possibly using - * @return string Converted string - * @access private - */ - function _autoconvert_encoding($input, $supposed_encoding = "UTF-8") { - $input_converted = $input; - - if (function_exists("mb_detect_order")) { - $mb_order = array_merge(array($supposed_encoding), mb_detect_order()); - set_error_handler('Mail_mimeDecode::_iconv_notice_handler'); - - // Default value in case of error - $detected_encoding = $supposed_encoding; - - try { - $detected_encoding = mb_detect_encoding($input, $mb_order, true); - // In some cases mb_detect_encoding returns an empty string - if ($detected_encoding === false || strlen($detected_encoding) == 0) { - $detected_encoding = $supposed_encoding; - } - $input_converted = iconv($detected_encoding, $this->_charset, $input); - } - catch(Exception $ex) { - $this->raiseError($ex->getMessage()); - } - restore_error_handler(); - - if ($input_converted === false || mb_strlen($input_converted, $this->_charset) !== mb_strlen($input, $detected_encoding)) { - ZLog::Write(LOGLEVEL_DEBUG, "Mail_mimeDecode()::_autoconvert_encoding(): Text cannot be correctly decoded, using original text. This will be ok if the part is not text, otherwise expect encoding errors"); - $input_converted = $input; - } - } - - return $input_converted; - } - - /** - * Given a quoted-printable string, this - * function will decode and return it. - * - * @param string Input body to decode - * @return string Decoded body - * @access private - */ - function _quotedPrintableDecode($input) - { - // Remove soft line breaks - $input = preg_replace("/=\r?\n/", '', $input); - - // Replace encoded characters - - $cb = create_function('$matches', ' return chr(hexdec($matches[0]));'); - - $input = preg_replace_callback( '/=([a-f0-9]{2})/i', $cb, $input); - - return $input; - } - - /** - * Checks the input for uuencoded files and returns - * an array of them. Can be called statically, eg: - * - * $files =& Mail_mimeDecode::uudecode($some_text); - * - * It will check for the begin 666 ... end syntax - * however and won't just blindly decode whatever you - * pass it. - * - * @param string Input body to look for attahcments in - * @return array Decoded bodies, filenames and permissions - * @access public - * @author Unknown - */ - function &uudecode($input) - { - // Find all uuencoded sections - preg_match_all("/begin ([0-7]{3}) (.+)\r?\n(.+)\r?\nend/Us", $input, $matches); - - for ($j = 0; $j < count($matches[3]); $j++) { - - $str = $matches[3][$j]; - $filename = $matches[2][$j]; - $fileperm = $matches[1][$j]; - - $file = ''; - $str = preg_split("/\r?\n/", trim($str)); - $strlen = count($str); - - for ($i = 0; $i < $strlen; $i++) { - $pos = 1; - $d = 0; - $len=(int)(((ord(substr($str[$i],0,1)) -32) - ' ') & 077); - - while (($d + 3 <= $len) AND ($pos + 4 <= strlen($str[$i]))) { - $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); - $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); - $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); - $c3 = (ord(substr($str[$i],$pos+3,1)) ^ 0x20); - $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); - - $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); - - $file .= chr(((($c2 - ' ') & 077) << 6) | (($c3 - ' ') & 077)); - - $pos += 4; - $d += 3; - } - - if (($d + 2 <= $len) && ($pos + 3 <= strlen($str[$i]))) { - $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); - $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); - $c2 = (ord(substr($str[$i],$pos+2,1)) ^ 0x20); - $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); - - $file .= chr(((($c1 - ' ') & 077) << 4) | ((($c2 - ' ') & 077) >> 2)); - - $pos += 3; - $d += 2; - } - - if (($d + 1 <= $len) && ($pos + 2 <= strlen($str[$i]))) { - $c0 = (ord(substr($str[$i],$pos,1)) ^ 0x20); - $c1 = (ord(substr($str[$i],$pos+1,1)) ^ 0x20); - $file .= chr(((($c0 - ' ') & 077) << 2) | ((($c1 - ' ') & 077) >> 4)); - - } - } - $files[] = array('filename' => $filename, 'fileperm' => $fileperm, 'filedata' => $file); - } - - return $files; - } - - /** - * Get all parts in the message with specified type and concatenate them together, unless the - * Content-Disposition is 'attachment', in which case the text is apparently an attachment - * - * @param string $message mimedecode message(part) - * @param string $message message subtype - * @param string &$body body reference - * @param boolean $replace_nr replace \n\r with \n - * - * @return void - * @access public - */ - static function getBodyRecursive($message, $subtype, &$body, $replace_nr = false) { - if(!isset($message->ctype_primary)) return; - if(strcasecmp($message->ctype_primary, "text") == 0 && strcasecmp($message->ctype_secondary, $subtype) == 0 && isset($message->body)) { - if ($replace_nr) { - $body .= str_replace("\n", "\r\n", str_replace("\r", "", $message->body)); - } - else { - $body .= $message->body; - } - } - - if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) { - foreach($message->parts as $part) { - // Check testing/samples/m1009.txt - // Content-Type: text/plain; charset=us-ascii; name="hareandtoroise.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="hareandtoroise.txt" - // We don't want to show that file text (outlook doesn't show it), so if we have content-disposition we don't apply recursivity - if(!isset($part->disposition)) { - Mail_mimeDecode::getBodyRecursive($part, $subtype, $body, $replace_nr); - } - } - } - } - - /** - * getSendArray() returns the arguments required for Mail::send() - * used to build the arguments for a mail::send() call - * - * Usage: - * $mailtext = Full email (for example generated by a template) - * $decoder = new Mail_mimeDecode($mailtext); - * $parts = $decoder->getSendArray(); - * if (!PEAR::isError($parts) { - * list($recipents,$headers,$body) = $parts; - * $mail = Mail::factory('smtp'); - * $mail->send($recipents,$headers,$body); - * } else { - * echo $parts->message; - * } - * @return mixed array of recipeint, headers,body or Pear_Error - * @access public - * @author Alan Knowles - */ - function getSendArray() - { - // prevent warning if this is not set - $this->_decode_headers = FALSE; - $headerlist =$this->_parseHeaders($this->_header); - $to = ""; - if (!$headerlist) { - return $this->raiseError("Message did not contain headers"); - } - foreach($headerlist as $item) { - $header[$item['name']] = $item['value']; - switch (strtolower($item['name'])) { - case "to": - case "cc": - case "bcc": - $to .= ",".$item['value']; - default: - break; - } - } - if ($to == "") { - return $this->raiseError("Message did not contain any recipents"); - } - $to = substr($to,1); - return array($to,$header,$this->_body); - } - - /** - * Returns a xml copy of the output of - * Mail_mimeDecode::decode. Pass the output in as the - * argument. This function can be called statically. Eg: - * - * $output = $obj->decode(); - * $xml = Mail_mimeDecode::getXML($output); - * - * The DTD used for this should have been in the package. Or - * alternatively you can get it from cvs, or here: - * http://www.phpguru.org/xmail/xmail.dtd. - * - * @param object Input to convert to xml. This should be the - * output of the Mail_mimeDecode::decode function - * @return string XML version of input - * @access public - */ - function getXML($input) - { - $crlf = "\r\n"; - $output = '' . $crlf . - '' . $crlf . - '' . $crlf . - Mail_mimeDecode::_getXML($input) . - ''; - - return $output; - } - - /** - * Function that does the actual conversion to xml. Does a single - * mimepart at a time. - * - * @param object Input to convert to xml. This is a mimepart object. - * It may or may not contain subparts. - * @param integer Number of tabs to indent - * @return string XML version of input - * @access private - */ - function _getXML($input, $indent = 1) - { - $htab = "\t"; - $crlf = "\r\n"; - $output = ''; - $headers = @(array)$input->headers; - - foreach ($headers as $hdr_name => $hdr_value) { - - // Multiple headers with this name - if (is_array($headers[$hdr_name])) { - for ($i = 0; $i < count($hdr_value); $i++) { - $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value[$i], $indent); - } - - // Only one header of this sort - } else { - $output .= Mail_mimeDecode::_getXML_helper($hdr_name, $hdr_value, $indent); - } - } - - if (!empty($input->parts)) { - for ($i = 0; $i < count($input->parts); $i++) { - $output .= $crlf . str_repeat($htab, $indent) . '' . $crlf . - Mail_mimeDecode::_getXML($input->parts[$i], $indent+1) . - str_repeat($htab, $indent) . '' . $crlf; - } - } elseif (isset($input->body)) { - $output .= $crlf . str_repeat($htab, $indent) . 'body . ']]>' . $crlf; - } - - return $output; - } - - /** - * Helper function to _getXML(). Returns xml of a header. - * - * @param string Name of header - * @param string Value of header - * @param integer Number of tabs to indent - * @return string XML version of input - * @access private - */ - function _getXML_helper($hdr_name, $hdr_value, $indent) - { - $htab = "\t"; - $crlf = "\r\n"; - $return = ''; - - $new_hdr_value = ($hdr_name != 'received') ? Mail_mimeDecode::_parseHeaderValue($hdr_value) : array('value' => $hdr_value); - $new_hdr_name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $hdr_name))); - - // Sort out any parameters - if (!empty($new_hdr_value['other'])) { - foreach ($new_hdr_value['other'] as $paramname => $paramvalue) { - $params[] = str_repeat($htab, $indent) . $htab . '' . $crlf . - str_repeat($htab, $indent) . $htab . $htab . '' . htmlspecialchars($paramname) . '' . $crlf . - str_repeat($htab, $indent) . $htab . $htab . '' . htmlspecialchars($paramvalue) . '' . $crlf . - str_repeat($htab, $indent) . $htab . '' . $crlf; - } - - $params = implode('', $params); - } else { - $params = ''; - } - - $return = str_repeat($htab, $indent) . '
' . $crlf . - str_repeat($htab, $indent) . $htab . '' . htmlspecialchars($new_hdr_name) . '' . $crlf . - str_repeat($htab, $indent) . $htab . '' . htmlspecialchars($new_hdr_value['value']) . '' . $crlf . - $params . - str_repeat($htab, $indent) . '
' . $crlf; - - return $return; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "mimeDecode error: ". $message); - return false; - } - -} // End of class diff --git a/sources/include/mimePart.php b/sources/include/mimePart.php deleted file mode 100644 index 1c3f689..0000000 --- a/sources/include/mimePart.php +++ /dev/null @@ -1,1270 +0,0 @@ - - * Copyright (c) 2003-2006, PEAR - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - Neither the name of the authors, nor the names of its contributors - * may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - * - * @category Mail - * @package Mail_Mime - * @author Richard Heyes - * @author Cipriano Groenendal - * @author Sean Coates - * @author Aleksander Machniak - * @copyright 2003-2006 PEAR - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version CVS: $Id$ - * @link http://pear.php.net/package/Mail_mime - */ - - -/** - * Z-Push changes - * - * removed PEAR dependency by implementing own raiseError() - * - * Reference implementation used: - * http://download.pear.php.net/package/Mail_Mime-1.8.9.tgz - * - * - */ - -/** - * The Mail_mimePart class is used to create MIME E-mail messages - * - * This class enables you to manipulate and build a mime email - * from the ground up. The Mail_Mime class is a userfriendly api - * to this class for people who aren't interested in the internals - * of mime mail. - * This class however allows full control over the email. - * - * @category Mail - * @package Mail_Mime - * @author Richard Heyes - * @author Cipriano Groenendal - * @author Sean Coates - * @author Aleksander Machniak - * @copyright 2003-2006 PEAR - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: @package_version@ - * @link http://pear.php.net/package/Mail_mime - */ -class Mail_mimePart -{ - /** - * The encoding type of this part - * - * @var string - * @access private - */ - var $_encoding; - - /** - * An array of subparts - * - * @var array - * @access private - */ - var $_subparts; - - /** - * The output of this part after being built - * - * @var string - * @access private - */ - var $_encoded; - - /** - * Headers for this part - * - * @var array - * @access private - */ - var $_headers; - - /** - * The body of this part (not encoded) - * - * @var string - * @access private - */ - var $_body; - - /** - * The location of file with body of this part (not encoded) - * - * @var string - * @access private - */ - var $_body_file; - - /** - * The end-of-line sequence - * - * @var string - * @access private - */ - var $_eol = "\r\n"; - - - /** - * Constructor. - * - * Sets up the object. - * - * @param string $body The body of the mime part if any. - * @param array $params An associative array of optional parameters: - * content_type - The content type for this part eg multipart/mixed - * encoding - The encoding to use, 7bit, 8bit, - * base64, or quoted-printable - * charset - Content character set - * cid - Content ID to apply - * disposition - Content disposition, inline or attachment - * filename - Filename parameter for content disposition - * description - Content description - * name_encoding - Encoding of the attachment name (Content-Type) - * By default filenames are encoded using RFC2231 - * Here you can set RFC2047 encoding (quoted-printable - * or base64) instead - * filename_encoding - Encoding of the attachment filename (Content-Disposition) - * See 'name_encoding' - * headers_charset - Charset of the headers e.g. filename, description. - * If not set, 'charset' will be used - * eol - End of line sequence. Default: "\r\n" - * headers - Hash array with additional part headers. Array keys can be - * in form of : - * body_file - Location of file with part's body (instead of $body) - * - * @access public - */ - function Mail_mimePart($body = '', $params = array()) - { - if (!empty($params['eol'])) { - $this->_eol = $params['eol']; - } else if (defined('MAIL_MIMEPART_CRLF')) { // backward-copat. - $this->_eol = MAIL_MIMEPART_CRLF; - } - - // Additional part headers - if (!empty($params['headers']) && is_array($params['headers'])) { - $headers = $params['headers']; - } - - foreach ($params as $key => $value) { - switch ($key) { - case 'encoding': - $this->_encoding = $value; - $headers['Content-Transfer-Encoding'] = $value; - break; - - case 'cid': - $headers['Content-ID'] = '<' . $value . '>'; - break; - - case 'location': - $headers['Content-Location'] = $value; - break; - - case 'body_file': - $this->_body_file = $value; - break; - - // for backward compatibility - case 'dfilename': - $params['filename'] = $value; - break; - } - } - - // Default content-type - if (empty($params['content_type'])) { - $params['content_type'] = 'text/plain'; - } - - // Content-Type - $headers['Content-Type'] = $params['content_type']; - if (!empty($params['charset'])) { - $charset = "charset={$params['charset']}"; - // place charset parameter in the same line, if possible - if ((strlen($headers['Content-Type']) + strlen($charset) + 16) <= 76) { - $headers['Content-Type'] .= '; '; - } else { - $headers['Content-Type'] .= ';' . $this->_eol . ' '; - } - $headers['Content-Type'] .= $charset; - - // Default headers charset - if (!isset($params['headers_charset'])) { - $params['headers_charset'] = $params['charset']; - } - } - - // header values encoding parameters - $h_charset = !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII'; - $h_language = !empty($params['language']) ? $params['language'] : null; - $h_encoding = !empty($params['name_encoding']) ? $params['name_encoding'] : null; - - - if (!empty($params['filename'])) { - $headers['Content-Type'] .= ';' . $this->_eol; - $headers['Content-Type'] .= $this->_buildHeaderParam( - 'name', $params['filename'], $h_charset, $h_language, $h_encoding - ); - } - - // Content-Disposition - if (!empty($params['disposition'])) { - $headers['Content-Disposition'] = $params['disposition']; - if (!empty($params['filename'])) { - $headers['Content-Disposition'] .= ';' . $this->_eol; - $headers['Content-Disposition'] .= $this->_buildHeaderParam( - 'filename', $params['filename'], $h_charset, $h_language, - !empty($params['filename_encoding']) ? $params['filename_encoding'] : null - ); - } - - // add attachment size - $size = $this->_body_file ? filesize($this->_body_file) : strlen($body); - if ($size) { - $headers['Content-Disposition'] .= ';' . $this->_eol . ' size=' . $size; - } - } - - if (!empty($params['description'])) { - $headers['Content-Description'] = $this->encodeHeader( - 'Content-Description', $params['description'], $h_charset, $h_encoding, - $this->_eol - ); - } - - // Search and add existing headers' parameters - foreach ($headers as $key => $value) { - $items = explode(':', $key); - if (count($items) == 2) { - $header = $items[0]; - $param = $items[1]; - if (isset($headers[$header])) { - $headers[$header] .= ';' . $this->_eol; - } - $headers[$header] .= $this->_buildHeaderParam( - $param, $value, $h_charset, $h_language, $h_encoding - ); - unset($headers[$key]); - } - } - - // Default encoding - if (!isset($this->_encoding)) { - $this->_encoding = '7bit'; - } - - // Assign stuff to member variables - $this->_encoded = array(); - $this->_headers = $headers; - $this->_body = $body; - } - - /** - * Encodes and returns the email. Also stores - * it in the encoded member variable - * - * @param string $boundary Pre-defined boundary string - * - * @return An associative array containing two elements, - * body and headers. The headers element is itself - * an indexed array. On error returns PEAR error object. - * @access public - */ - function encode($boundary=null) - { - $encoded =& $this->_encoded; - - if (count($this->_subparts)) { - $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); - $eol = $this->_eol; - - $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; - - $encoded['body'] = ''; - - for ($i = 0; $i < count($this->_subparts); $i++) { - $encoded['body'] .= '--' . $boundary . $eol; - $tmp = $this->_subparts[$i]->encode(); - if ($this->_isError($tmp)) { - return $tmp; - } - foreach ($tmp['headers'] as $key => $value) { - $encoded['body'] .= $key . ': ' . $value . $eol; - } - $encoded['body'] .= $eol . $tmp['body'] . $eol; - } - - $encoded['body'] .= '--' . $boundary . '--' . $eol; - - } else if ($this->_body) { - $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding); - } else if ($this->_body_file) { - // Temporarily reset magic_quotes_runtime for file reads and writes - if ($magic_quote_setting = get_magic_quotes_runtime()) { - @ini_set('magic_quotes_runtime', 0); - } - $body = $this->_getEncodedDataFromFile($this->_body_file, $this->_encoding); - if ($magic_quote_setting) { - @ini_set('magic_quotes_runtime', $magic_quote_setting); - } - - if ($this->_isError($body)) { - return $body; - } - $encoded['body'] = $body; - } else { - $encoded['body'] = ''; - } - - // Add headers to $encoded - $encoded['headers'] =& $this->_headers; - - return $encoded; - } - - /** - * Encodes and saves the email into file. File must exist. - * Data will be appended to the file. - * - * @param string $filename Output file location - * @param string $boundary Pre-defined boundary string - * @param boolean $skip_head True if you don't want to save headers - * - * @return array An associative array containing message headers - * or PEAR error object - * @access public - * @since 1.6.0 - */ - function encodeToFile($filename, $boundary=null, $skip_head=false) - { - if (file_exists($filename) && !is_writable($filename)) { - $err = $this->_raiseError('File is not writeable: ' . $filename); - return $err; - } - - if (!($fh = fopen($filename, 'ab'))) { - $err = $this->_raiseError('Unable to open file: ' . $filename); - return $err; - } - - // Temporarily reset magic_quotes_runtime for file reads and writes - if ($magic_quote_setting = get_magic_quotes_runtime()) { - @ini_set('magic_quotes_runtime', 0); - } - - $res = $this->_encodePartToFile($fh, $boundary, $skip_head); - - fclose($fh); - - if ($magic_quote_setting) { - @ini_set('magic_quotes_runtime', $magic_quote_setting); - } - - return $this->_isError($res) ? $res : $this->_headers; - } - - /** - * Encodes given email part into file - * - * @param string $fh Output file handle - * @param string $boundary Pre-defined boundary string - * @param boolean $skip_head True if you don't want to save headers - * - * @return array True on sucess or PEAR error object - * @access private - */ - function _encodePartToFile($fh, $boundary=null, $skip_head=false) - { - $eol = $this->_eol; - - if (count($this->_subparts)) { - $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); - $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; - } - - if (!$skip_head) { - foreach ($this->_headers as $key => $value) { - fwrite($fh, $key . ': ' . $value . $eol); - } - $f_eol = $eol; - } else { - $f_eol = ''; - } - - if (count($this->_subparts)) { - for ($i = 0; $i < count($this->_subparts); $i++) { - fwrite($fh, $f_eol . '--' . $boundary . $eol); - $res = $this->_subparts[$i]->_encodePartToFile($fh); - if ($this->_isError($res)) { - return $res; - } - $f_eol = $eol; - } - - fwrite($fh, $eol . '--' . $boundary . '--' . $eol); - - } else if ($this->_body) { - fwrite($fh, $f_eol . $this->_getEncodedData($this->_body, $this->_encoding)); - } else if ($this->_body_file) { - fwrite($fh, $f_eol); - $res = $this->_getEncodedDataFromFile( - $this->_body_file, $this->_encoding, $fh - ); - if ($this->_isError($res)) { - return $res; - } - } - - return true; - } - - /** - * Adds a subpart to current mime part and returns - * a reference to it - * - * @param string $body The body of the subpart, if any. - * @param array $params The parameters for the subpart, same - * as the $params argument for constructor. - * - * @return Mail_mimePart A reference to the part you just added. In PHP4, it is - * crucial if using multipart/* in your subparts that - * you use =& in your script when calling this function, - * otherwise you will not be able to add further subparts. - * @access public - */ - function &addSubpart($body, $params) - { - $this->_subparts[] = $part = new Mail_mimePart($body, $params); - return $part; - } - - /** - * Returns encoded data based upon encoding passed to it - * - * @param string $data The data to encode. - * @param string $encoding The encoding type to use, 7bit, base64, - * or quoted-printable. - * - * @return string - * @access private - */ - function _getEncodedData($data, $encoding) - { - switch ($encoding) { - case 'quoted-printable': - return $this->_quotedPrintableEncode($data); - break; - - case 'base64': - return rtrim(chunk_split(base64_encode($data), 76, $this->_eol)); - break; - - case '8bit': - case '7bit': - default: - return $data; - } - } - - /** - * Returns encoded data based upon encoding passed to it - * - * @param string $filename Data file location - * @param string $encoding The encoding type to use, 7bit, base64, - * or quoted-printable. - * @param resource $fh Output file handle. If set, data will be - * stored into it instead of returning it - * - * @return string Encoded data or PEAR error object - * @access private - */ - function _getEncodedDataFromFile($filename, $encoding, $fh=null) - { - if (!is_readable($filename)) { - $err = $this->_raiseError('Unable to read file: ' . $filename); - return $err; - } - - if (!($fd = fopen($filename, 'rb'))) { - $err = $this->_raiseError('Could not open file: ' . $filename); - return $err; - } - - $data = ''; - - switch ($encoding) { - case 'quoted-printable': - while (!feof($fd)) { - $buffer = $this->_quotedPrintableEncode(fgets($fd)); - if ($fh) { - fwrite($fh, $buffer); - } else { - $data .= $buffer; - } - } - break; - - case 'base64': - while (!feof($fd)) { - // Should read in a multiple of 57 bytes so that - // the output is 76 bytes per line. Don't use big chunks - // because base64 encoding is memory expensive - $buffer = fread($fd, 57 * 9198); // ca. 0.5 MB - $buffer = base64_encode($buffer); - $buffer = chunk_split($buffer, 76, $this->_eol); - if (feof($fd)) { - $buffer = rtrim($buffer); - } - - if ($fh) { - fwrite($fh, $buffer); - } else { - $data .= $buffer; - } - } - break; - - case '8bit': - case '7bit': - default: - while (!feof($fd)) { - $buffer = fread($fd, 1048576); // 1 MB - if ($fh) { - fwrite($fh, $buffer); - } else { - $data .= $buffer; - } - } - } - - fclose($fd); - - if (!$fh) { - return $data; - } - } - - /** - * Encodes data to quoted-printable standard. - * - * @param string $input The data to encode - * @param int $line_max Optional max line length. Should - * not be more than 76 chars - * - * @return string Encoded data - * - * @access private - */ - function _quotedPrintableEncode($input , $line_max = 76) - { - $eol = $this->_eol; - /* - // imap_8bit() is extremely fast, but doesn't handle properly some characters - if (function_exists('imap_8bit') && $line_max == 76) { - $input = preg_replace('/\r?\n/', "\r\n", $input); - $input = imap_8bit($input); - if ($eol != "\r\n") { - $input = str_replace("\r\n", $eol, $input); - } - return $input; - } - */ - $lines = preg_split("/\r?\n/", $input); - $escape = '='; - $output = ''; - - while (list($idx, $line) = each($lines)) { - $newline = ''; - $i = 0; - - while (isset($line[$i])) { - $char = $line[$i]; - $dec = ord($char); - $i++; - - if (($dec == 32) && (!isset($line[$i]))) { - // convert space at eol only - $char = '=20'; - } elseif ($dec == 9 && isset($line[$i])) { - ; // Do nothing if a TAB is not on eol - } elseif (($dec == 61) || ($dec < 32) || ($dec > 126)) { - $char = $escape . sprintf('%02X', $dec); - } elseif (($dec == 46) && (($newline == '') - || ((strlen($newline) + strlen("=2E")) >= $line_max)) - ) { - // Bug #9722: convert full-stop at bol, - // some Windows servers need this, won't break anything (cipri) - // Bug #11731: full-stop at bol also needs to be encoded - // if this line would push us over the line_max limit. - $char = '=2E'; - } - - // Note, when changing this line, also change the ($dec == 46) - // check line, as it mimics this line due to Bug #11731 - // EOL is not counted - if ((strlen($newline) + strlen($char)) >= $line_max) { - // soft line break; " =\r\n" is okay - $output .= $newline . $escape . $eol; - $newline = ''; - } - $newline .= $char; - } // end of for - $output .= $newline . $eol; - unset($lines[$idx]); - } - // Don't want last crlf - $output = substr($output, 0, -1 * strlen($eol)); - return $output; - } - - /** - * Encodes the parameter of a header. - * - * @param string $name The name of the header-parameter - * @param string $value The value of the paramter - * @param string $charset The characterset of $value - * @param string $language The language used in $value - * @param string $encoding Parameter encoding. If not set, parameter value - * is encoded according to RFC2231 - * @param int $maxLength The maximum length of a line. Defauls to 75 - * - * @return string - * - * @access private - */ - function _buildHeaderParam($name, $value, $charset=null, $language=null, - $encoding=null, $maxLength=75 - ) { - // RFC 2045: - // value needs encoding if contains non-ASCII chars or is longer than 78 chars - if (!preg_match('#[^\x20-\x7E]#', $value)) { - $token_regexp = '#([^\x21\x23-\x27\x2A\x2B\x2D' - . '\x2E\x30-\x39\x41-\x5A\x5E-\x7E])#'; - if (!preg_match($token_regexp, $value)) { - // token - if (strlen($name) + strlen($value) + 3 <= $maxLength) { - return " {$name}={$value}"; - } - } else { - // quoted-string - $quoted = addcslashes($value, '\\"'); - if (strlen($name) + strlen($quoted) + 5 <= $maxLength) { - return " {$name}=\"{$quoted}\""; - } - } - } - - // RFC2047: use quoted-printable/base64 encoding - if ($encoding == 'quoted-printable' || $encoding == 'base64') { - return $this->_buildRFC2047Param($name, $value, $charset, $encoding); - } - - // RFC2231: - $encValue = preg_replace_callback( - '/([^\x21\x23\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E])/', - array($this, '_encodeReplaceCallback'), $value - ); - $value = "$charset'$language'$encValue"; - - $header = " {$name}*={$value}"; - if (strlen($header) <= $maxLength) { - return $header; - } - - $preLength = strlen(" {$name}*0*="); - $maxLength = max(16, $maxLength - $preLength - 3); - $maxLengthReg = "|(.{0,$maxLength}[^\%][^\%])|"; - - $headers = array(); - $headCount = 0; - while ($value) { - $matches = array(); - $found = preg_match($maxLengthReg, $value, $matches); - if ($found) { - $headers[] = " {$name}*{$headCount}*={$matches[0]}"; - $value = substr($value, strlen($matches[0])); - } else { - $headers[] = " {$name}*{$headCount}*={$value}"; - $value = ''; - } - $headCount++; - } - - $headers = implode(';' . $this->_eol, $headers); - return $headers; - } - - /** - * Encodes header parameter as per RFC2047 if needed - * - * @param string $name The parameter name - * @param string $value The parameter value - * @param string $charset The parameter charset - * @param string $encoding Encoding type (quoted-printable or base64) - * @param int $maxLength Encoded parameter max length. Default: 76 - * - * @return string Parameter line - * @access private - */ - function _buildRFC2047Param($name, $value, $charset, - $encoding='quoted-printable', $maxLength=76 - ) { - // WARNING: RFC 2047 says: "An 'encoded-word' MUST NOT be used in - // parameter of a MIME Content-Type or Content-Disposition field", - // but... it's supported by many clients/servers - $quoted = ''; - - if ($encoding == 'base64') { - $value = base64_encode($value); - $prefix = '=?' . $charset . '?B?'; - $suffix = '?='; - - // 2 x SPACE, 2 x '"', '=', ';' - $add_len = strlen($prefix . $suffix) + strlen($name) + 6; - $len = $add_len + strlen($value); - - while ($len > $maxLength) { - // We can cut base64-encoded string every 4 characters - $real_len = floor(($maxLength - $add_len) / 4) * 4; - $_quote = substr($value, 0, $real_len); - $value = substr($value, $real_len); - - $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; - $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' - $len = strlen($value) + $add_len; - } - $quoted .= $prefix . $value . $suffix; - - } else { - // quoted-printable - $value = $this->encodeQP($value); - $prefix = '=?' . $charset . '?Q?'; - $suffix = '?='; - - // 2 x SPACE, 2 x '"', '=', ';' - $add_len = strlen($prefix . $suffix) + strlen($name) + 6; - $len = $add_len + strlen($value); - - while ($len > $maxLength) { - $length = $maxLength - $add_len; - // don't break any encoded letters - if (preg_match("/^(.{0,$length}[^\=][^\=])/", $value, $matches)) { - $_quote = $matches[1]; - } - - $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; - $value = substr($value, strlen($_quote)); - $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' - $len = strlen($value) + $add_len; - } - - $quoted .= $prefix . $value . $suffix; - } - - return " {$name}=\"{$quoted}\""; - } - - /** - * Encodes a header as per RFC2047 - * - * @param string $name The header name - * @param string $value The header data to encode - * @param string $charset Character set name - * @param string $encoding Encoding name (base64 or quoted-printable) - * @param string $eol End-of-line sequence. Default: "\r\n" - * - * @return string Encoded header data (without a name) - * @access public - * @since 1.6.1 - */ - function encodeHeader($name, $value, $charset='ISO-8859-1', - $encoding='quoted-printable', $eol="\r\n" - ) { - // Structured headers - $comma_headers = array( - 'from', 'to', 'cc', 'bcc', 'sender', 'reply-to', - 'resent-from', 'resent-to', 'resent-cc', 'resent-bcc', - 'resent-sender', 'resent-reply-to', - 'mail-reply-to', 'mail-followup-to', - 'return-receipt-to', 'disposition-notification-to', - ); - $other_headers = array( - 'references', 'in-reply-to', 'message-id', 'resent-message-id', - ); - - $name = strtolower($name); - - if (in_array($name, $comma_headers)) { - $separator = ','; - } else if (in_array($name, $other_headers)) { - $separator = ' '; - } - - if (!$charset) { - $charset = 'ISO-8859-1'; - } - - // Structured header (make sure addr-spec inside is not encoded) - if (!empty($separator)) { - // Simple e-mail address regexp - $email_regexp = '([^\s<]+|("[^\r\n"]+"))@\S+'; - - $parts = Mail_mimePart::_explodeQuotedString("[\t$separator]", $value); - $value = ''; - - foreach ($parts as $part) { - $part = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $part); - $part = trim($part); - - if (!$part) { - continue; - } - if ($value) { - $value .= $separator == ',' ? $separator . ' ' : ' '; - } else { - $value = $name . ': '; - } - - // let's find phrase (name) and/or addr-spec - if (preg_match('/^<' . $email_regexp . '>$/', $part)) { - $value .= $part; - } else if (preg_match('/^' . $email_regexp . '$/', $part)) { - // address without brackets and without name - $value .= $part; - } else if (preg_match('/<*' . $email_regexp . '>*$/', $part, $matches)) { - // address with name (handle name) - $address = $matches[0]; - $word = str_replace($address, '', $part); - $word = trim($word); - // check if phrase requires quoting - if ($word) { - // non-ASCII: require encoding - if (preg_match('#([^\s\x21-\x7E]){1}#', $word)) { - if ($word[0] == '"' && $word[strlen($word)-1] == '"') { - // de-quote quoted-string, encoding changes - // string to atom - $search = array("\\\"", "\\\\"); - $replace = array("\"", "\\"); - $word = str_replace($search, $replace, $word); - $word = substr($word, 1, -1); - } - // find length of last line - if (($pos = strrpos($value, $eol)) !== false) { - $last_len = strlen($value) - $pos; - } else { - $last_len = strlen($value); - } - $word = Mail_mimePart::encodeHeaderValue( - $word, $charset, $encoding, $last_len, $eol - ); - } else if (($word[0] != '"' || $word[strlen($word)-1] != '"') - && preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $word) - ) { - // ASCII: quote string if needed - $word = '"'.addcslashes($word, '\\"').'"'; - } - } - $value .= $word.' '.$address; - } else { - // addr-spec not found, don't encode (?) - $value .= $part; - } - - // RFC2822 recommends 78 characters limit, use 76 from RFC2047 - $value = wordwrap($value, 76, $eol . ' '); - } - - // remove header name prefix (there could be EOL too) - $value = preg_replace( - '/^'.$name.':('.preg_quote($eol, '/').')* /', '', $value - ); - } else { - // Unstructured header - // non-ASCII: require encoding - if (preg_match('#([^\s\x21-\x7E]){1}#', $value)) { - if ($value[0] == '"' && $value[strlen($value)-1] == '"') { - // de-quote quoted-string, encoding changes - // string to atom - $search = array("\\\"", "\\\\"); - $replace = array("\"", "\\"); - $value = str_replace($search, $replace, $value); - $value = substr($value, 1, -1); - } - $value = Mail_mimePart::encodeHeaderValue( - $value, $charset, $encoding, strlen($name) + 2, $eol - ); - } else if (strlen($name.': '.$value) > 78) { - // ASCII: check if header line isn't too long and use folding - $value = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $value); - $tmp = wordwrap($name.': '.$value, 78, $eol . ' '); - $value = preg_replace('/^'.$name.':\s*/', '', $tmp); - // hard limit 998 (RFC2822) - $value = wordwrap($value, 998, $eol . ' ', true); - } - } - - return $value; - } - - /** - * Explode quoted string - * - * @param string $delimiter Delimiter expression string for preg_match() - * @param string $string Input string - * - * @return array String tokens array - * @access private - */ - function _explodeQuotedString($delimiter, $string) - { - $result = array(); - $strlen = strlen($string); - - for ($q=$p=$i=0; $i < $strlen; $i++) { - if ($string[$i] == "\"" - && (empty($string[$i-1]) || $string[$i-1] != "\\") - ) { - $q = $q ? false : true; - } else if (!$q && preg_match("/$delimiter/", $string[$i])) { - $result[] = substr($string, $p, $i - $p); - $p = $i + 1; - } - } - - $result[] = substr($string, $p); - return $result; - } - - /** - * Encodes a header value as per RFC2047 - * - * @param string $value The header data to encode - * @param string $charset Character set name - * @param string $encoding Encoding name (base64 or quoted-printable) - * @param int $prefix_len Prefix length. Default: 0 - * @param string $eol End-of-line sequence. Default: "\r\n" - * - * @return string Encoded header data - * @access public - * @since 1.6.1 - */ - function encodeHeaderValue($value, $charset, $encoding, $prefix_len=0, $eol="\r\n") - { - // #17311: Use multibyte aware method (requires mbstring extension) - if ($result = Mail_mimePart::encodeMB($value, $charset, $encoding, $prefix_len, $eol)) { - return $result; - } - - // Generate the header using the specified params and dynamicly - // determine the maximum length of such strings. - // 75 is the value specified in the RFC. - $encoding = $encoding == 'base64' ? 'B' : 'Q'; - $prefix = '=?' . $charset . '?' . $encoding .'?'; - $suffix = '?='; - $maxLength = 75 - strlen($prefix . $suffix); - $maxLength1stLine = $maxLength - $prefix_len; - - if ($encoding == 'B') { - // Base64 encode the entire string - $value = base64_encode($value); - - // We can cut base64 every 4 characters, so the real max - // we can get must be rounded down. - $maxLength = $maxLength - ($maxLength % 4); - $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4); - - $cutpoint = $maxLength1stLine; - $output = ''; - - while ($value) { - // Split translated string at every $maxLength - $part = substr($value, 0, $cutpoint); - $value = substr($value, $cutpoint); - $cutpoint = $maxLength; - // RFC 2047 specifies that any split header should - // be separated by a CRLF SPACE. - if ($output) { - $output .= $eol . ' '; - } - $output .= $prefix . $part . $suffix; - } - $value = $output; - } else { - // quoted-printable encoding has been selected - $value = Mail_mimePart::encodeQP($value); - - // This regexp will break QP-encoded text at every $maxLength - // but will not break any encoded letters. - $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|"; - $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|"; - - if (strlen($value) > $maxLength1stLine) { - // Begin with the regexp for the first line. - $reg = $reg1st; - $output = ''; - while ($value) { - // Split translated string at every $maxLength - // But make sure not to break any translated chars. - $found = preg_match($reg, $value, $matches); - - // After this first line, we need to use a different - // regexp for the first line. - $reg = $reg2nd; - - // Save the found part and encapsulate it in the - // prefix & suffix. Then remove the part from the - // $value_out variable. - if ($found) { - $part = $matches[0]; - $len = strlen($matches[0]); - $value = substr($value, $len); - } else { - $part = $value; - $value = ''; - } - - // RFC 2047 specifies that any split header should - // be separated by a CRLF SPACE - if ($output) { - $output .= $eol . ' '; - } - $output .= $prefix . $part . $suffix; - } - $value = $output; - } else { - $value = $prefix . $value . $suffix; - } - } - - return $value; - } - - /** - * Encodes the given string using quoted-printable - * - * @param string $str String to encode - * - * @return string Encoded string - * @access public - * @since 1.6.0 - */ - function encodeQP($str) - { - // Bug #17226 RFC 2047 restricts some characters - // if the word is inside a phrase, permitted chars are only: - // ASCII letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_" - - // "=", "_", "?" must be encoded - $regexp = '/([\x22-\x29\x2C\x2E\x3A-\x40\x5B-\x60\x7B-\x7E\x80-\xFF])/'; - $str = preg_replace_callback( - $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $str - ); - - return str_replace(' ', '_', $str); - } - - /** - * Encodes the given string using base64 or quoted-printable. - * This method makes sure that encoded-word represents an integral - * number of characters as per RFC2047. - * - * @param string $str String to encode - * @param string $charset Character set name - * @param string $encoding Encoding name (base64 or quoted-printable) - * @param int $prefix_len Prefix length. Default: 0 - * @param string $eol End-of-line sequence. Default: "\r\n" - * - * @return string Encoded string - * @access public - * @since 1.8.0 - */ - function encodeMB($str, $charset, $encoding, $prefix_len=0, $eol="\r\n") - { - if (!function_exists('mb_substr') || !function_exists('mb_strlen')) { - return; - } - - $encoding = $encoding == 'base64' ? 'B' : 'Q'; - // 75 is the value specified in the RFC - $prefix = '=?' . $charset . '?'.$encoding.'?'; - $suffix = '?='; - $maxLength = 75 - strlen($prefix . $suffix); - - // A multi-octet character may not be split across adjacent encoded-words - // So, we'll loop over each character - // mb_stlen() with wrong charset will generate a warning here and return null - $length = mb_strlen($str, $charset); - $result = ''; - $line_length = $prefix_len; - - if ($encoding == 'B') { - // base64 - $start = 0; - $prev = ''; - - for ($i=1; $i<=$length; $i++) { - // See #17311 - $chunk = mb_substr($str, $start, $i-$start, $charset); - $chunk = base64_encode($chunk); - $chunk_len = strlen($chunk); - - if ($line_length + $chunk_len == $maxLength || $i == $length) { - if ($result) { - $result .= "\n"; - } - $result .= $chunk; - $line_length = 0; - $start = $i; - } else if ($line_length + $chunk_len > $maxLength) { - if ($result) { - $result .= "\n"; - } - if ($prev) { - $result .= $prev; - } - $line_length = 0; - $start = $i - 1; - } else { - $prev = $chunk; - } - } - } else { - // quoted-printable - // see encodeQP() - $regexp = '/([\x22-\x29\x2C\x2E\x3A-\x40\x5B-\x60\x7B-\x7E\x80-\xFF])/'; - - for ($i=0; $i<=$length; $i++) { - $char = mb_substr($str, $i, 1, $charset); - // RFC recommends underline (instead of =20) in place of the space - // that's one of the reasons why we're not using iconv_mime_encode() - if ($char == ' ') { - $char = '_'; - $char_len = 1; - } else { - $char = preg_replace_callback( - $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $char - ); - $char_len = strlen($char); - } - - if ($line_length + $char_len > $maxLength) { - if ($result) { - $result .= "\n"; - } - $line_length = 0; - } - - $result .= $char; - $line_length += $char_len; - } - } - - if ($result) { - $result = $prefix - .str_replace("\n", $suffix.$eol.' '.$prefix, $result).$suffix; - } - - return $result; - } - - /** - * Callback function to replace extended characters (\x80-xFF) with their - * ASCII values (RFC2047: quoted-printable) - * - * @param array $matches Preg_replace's matches array - * - * @return string Encoded character string - * @access private - */ - function _qpReplaceCallback($matches) - { - return sprintf('=%02X', ord($matches[1])); - } - - /** - * Callback function to replace extended characters (\x80-xFF) with their - * ASCII values (RFC2231) - * - * @param array $matches Preg_replace's matches array - * - * @return string Encoded character string - * @access private - */ - function _encodeReplaceCallback($matches) - { - return sprintf('%%%02X', ord($matches[1])); - } - - /** - * PEAR::isError implementation - * - * @param mixed $data Object - * - * @return bool True if object is an instance of PEAR_Error - * @access private - */ - function _isError($data) - { - // PEAR::isError() is not PHP 5.4 compatible (see Bug #19473) - //if (is_object($data) && is_a($data, 'PEAR_Error')) { - // return true; - //} - - //return false; - return $data === false; - } - - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - function _raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "mimePart error: ". $message); - return false; - } -} // End of class diff --git a/sources/include/z_RFC822.php b/sources/include/z_RFC822.php deleted file mode 100644 index 739897f..0000000 --- a/sources/include/z_RFC822.php +++ /dev/null @@ -1,945 +0,0 @@ - - * @author Chuck Hagenbuch - * @author Chuck Hagenbuch - * @version $Revision$ - * @license BSD - * @package Mail - */ - -class Mail_RFC822 { - - /** - * The address being parsed by the RFC822 object. - * @var string $address - */ - var $address = ''; - - /** - * The default domain to use for unqualified addresses. - * @var string $default_domain - */ - var $default_domain = 'localhost'; - - /** - * Should we return a nested array showing groups, or flatten everything? - * @var boolean $nestGroups - */ - var $nestGroups = true; - - /** - * Whether or not to validate atoms for non-ascii characters. - * @var boolean $validate - */ - var $validate = true; - - /** - * The array of raw addresses built up as we parse. - * @var array $addresses - */ - var $addresses = array(); - - /** - * The final array of parsed address information that we build up. - * @var array $structure - */ - var $structure = array(); - - /** - * The current error message, if any. - * @var string $error - */ - var $error = null; - - /** - * An internal counter/pointer. - * @var integer $index - */ - var $index = null; - - /** - * The number of groups that have been found in the address list. - * @var integer $num_groups - */ - var $num_groups = 0; - - /** - * A variable so that we can tell whether or not we're inside a - * Mail_RFC822 object. - * @var boolean $mailRFC822 - */ - var $mailRFC822 = true; - - /** - * A limit after which processing stops - * @var int $limit - */ - var $limit = null; - - /** - * Sets up the object. The address must either be set here or when - * calling parseAddressList(). One or the other. - * - * @param string $address The address(es) to validate. - * @param string $default_domain Default domain/host etc. If not supplied, will be set to localhost. - * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing. - * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance. - * - * @return object Mail_RFC822 A new Mail_RFC822 object. - */ - public function __construct($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null) - { - if (isset($address)) $this->address = $address; - if (isset($default_domain)) $this->default_domain = $default_domain; - if (isset($nest_groups)) $this->nestGroups = $nest_groups; - if (isset($validate)) $this->validate = $validate; - if (isset($limit)) $this->limit = $limit; - } - - /** - * Starts the whole process. The address must either be set here - * or when creating the object. One or the other. - * - * @param string $address The address(es) to validate. - * @param string $default_domain Default domain/host etc. - * @param boolean $nest_groups Whether to return the structure with groups nested for easier viewing. - * @param boolean $validate Whether to validate atoms. Turn this off if you need to run addresses through before encoding the personal names, for instance. - * - * @return array A structured array of addresses. - */ - public function parseAddressList($address = null, $default_domain = null, $nest_groups = null, $validate = null, $limit = null) - { - if (!isset($this) || !isset($this->mailRFC822)) { - $obj = new Mail_RFC822($address, $default_domain, $nest_groups, $validate, $limit); - return $obj->parseAddressList(); - } - - if (isset($address)) $this->address = $address; - // z-push addition - if (strlen(trim($this->address)) == 0) return array(); - if (isset($default_domain)) $this->default_domain = $default_domain; - if (isset($nest_groups)) $this->nestGroups = $nest_groups; - if (isset($validate)) $this->validate = $validate; - if (isset($limit)) $this->limit = $limit; - - $this->structure = array(); - $this->addresses = array(); - $this->error = null; - $this->index = null; - - // Unfold any long lines in $this->address. - $this->address = preg_replace('/\r?\n/', "\r\n", $this->address); - $this->address = preg_replace('/\r\n(\t| )+/', ' ', $this->address); - - while ($this->address = $this->_splitAddresses($this->address)); - - if ($this->address === false || isset($this->error)) { - //require_once 'PEAR.php'; - return $this->raiseError($this->error); - } - - // Validate each address individually. If we encounter an invalid - // address, stop iterating and return an error immediately. - foreach ($this->addresses as $address) { - $valid = $this->_validateAddress($address); - - if ($valid === false || isset($this->error)) { - //require_once 'PEAR.php'; - return $this->raiseError($this->error); - } - - if (!$this->nestGroups) { - $this->structure = array_merge($this->structure, $valid); - } else { - $this->structure[] = $valid; - } - } - - return $this->structure; - } - - /** - * Splits an address into separate addresses. - * - * @param string $address The addresses to split. - * @return boolean Success or failure. - */ - protected function _splitAddresses($address) - { - if (!empty($this->limit) && count($this->addresses) == $this->limit) { - return ''; - } - - if ($this->_isGroup($address) && !isset($this->error)) { - $split_char = ';'; - $is_group = true; - } elseif (!isset($this->error)) { - $split_char = ','; - $is_group = false; - } elseif (isset($this->error)) { - return false; - } - - // Split the string based on the above ten or so lines. - $parts = explode($split_char, $address); - $string = $this->_splitCheck($parts, $split_char); - - // If a group... - if ($is_group) { - // If $string does not contain a colon outside of - // brackets/quotes etc then something's fubar. - - // First check there's a colon at all: - if (strpos($string, ':') === false) { - $this->error = 'Invalid address: ' . $string; - return false; - } - - // Now check it's outside of brackets/quotes: - if (!$this->_splitCheck(explode(':', $string), ':')) { - return false; - } - - // We must have a group at this point, so increase the counter: - $this->num_groups++; - } - - // $string now contains the first full address/group. - // Add to the addresses array. - $this->addresses[] = array( - 'address' => trim($string), - 'group' => $is_group - ); - - // Remove the now stored address from the initial line, the +1 - // is to account for the explode character. - $address = trim(substr($address, strlen($string) + 1)); - - // If the next char is a comma and this was a group, then - // there are more addresses, otherwise, if there are any more - // chars, then there is another address. - if ($is_group && substr($address, 0, 1) == ','){ - $address = trim(substr($address, 1)); - return $address; - - } elseif (strlen($address) > 0) { - return $address; - - } else { - return ''; - } - - // If you got here then something's off - return false; - } - - /** - * Checks for a group at the start of the string. - * - * @param string $address The address to check. - * @return boolean Whether or not there is a group at the start of the string. - */ - protected function _isGroup($address) - { - // First comma not in quotes, angles or escaped: - $parts = explode(',', $address); - $string = $this->_splitCheck($parts, ','); - - // Now we have the first address, we can reliably check for a - // group by searching for a colon that's not escaped or in - // quotes or angle brackets. - if (count($parts = explode(':', $string)) > 1) { - $string2 = $this->_splitCheck($parts, ':'); - return ($string2 !== $string); - } else { - return false; - } - } - - /** - * A common function that will check an exploded string. - * - * @param array $parts The exloded string. - * @param string $char The char that was exploded on. - * @return mixed False if the string contains unclosed quotes/brackets, or the string on success. - */ - protected function _splitCheck($parts, $char) - { - $string = $parts[0]; - - for ($i = 0; $i < count($parts); $i++) { - if ($this->_hasUnclosedQuotes($string) - || $this->_hasUnclosedBrackets($string, '<>') - || $this->_hasUnclosedBrackets($string, '[]') - || $this->_hasUnclosedBrackets($string, '()') - || substr($string, -1) == '\\') { - if (isset($parts[$i + 1])) { - $string = $string . $char . $parts[$i + 1]; - } else { - $this->error = 'Invalid address spec. Unclosed bracket or quotes'; - return false; - } - } else { - $this->index = $i; - break; - } - } - - return $string; - } - - /** - * Checks if a string has unclosed quotes or not. - * - * @param string $string The string to check. - * @return boolean True if there are unclosed quotes inside the string, - * false otherwise. - */ - protected function _hasUnclosedQuotes($string) - { - $string = trim($string); - $iMax = strlen($string); - $in_quote = false; - $i = $slashes = 0; - - for (; $i < $iMax; ++$i) { - switch ($string[$i]) { - case '\\': - ++$slashes; - break; - - case '"': - if ($slashes % 2 == 0) { - $in_quote = !$in_quote; - } - // Fall through to default action below. - - default: - $slashes = 0; - break; - } - } - - return $in_quote; - } - - /** - * Checks if a string has an unclosed brackets or not. IMPORTANT: - * This function handles both angle brackets and square brackets; - * - * @param string $string The string to check. - * @param string $chars The characters to check for. - * @return boolean True if there are unclosed brackets inside the string, false otherwise. - */ - protected function _hasUnclosedBrackets($string, $chars) - { - $num_angle_start = substr_count($string, $chars[0]); - $num_angle_end = substr_count($string, $chars[1]); - - $this->_hasUnclosedBracketsSub($string, $num_angle_start, $chars[0]); - $this->_hasUnclosedBracketsSub($string, $num_angle_end, $chars[1]); - - if ($num_angle_start < $num_angle_end) { - $this->error = 'Invalid address spec. Unmatched quote or bracket (' . $chars . ')'; - return false; - } else { - return ($num_angle_start > $num_angle_end); - } - } - - /** - * Sub function that is used only by hasUnclosedBrackets(). - * - * @param string $string The string to check. - * @param integer &$num The number of occurences. - * @param string $char The character to count. - * @return integer The number of occurences of $char in $string, adjusted for backslashes. - */ - protected function _hasUnclosedBracketsSub($string, &$num, $char) - { - $parts = explode($char, $string); - for ($i = 0; $i < count($parts); $i++){ - if (substr($parts[$i], -1) == '\\' || $this->_hasUnclosedQuotes($parts[$i])) - $num--; - if (isset($parts[$i + 1])) - $parts[$i + 1] = $parts[$i] . $char . $parts[$i + 1]; - } - - return $num; - } - - /** - * Function to begin checking the address. - * - * @param string $address The address to validate. - * @return mixed False on failure, or a structured array of address information on success. - */ - protected function _validateAddress($address) - { - $is_group = false; - $addresses = array(); - - if ($address['group']) { - $is_group = true; - - // Get the group part of the name - $parts = explode(':', $address['address']); - $groupname = $this->_splitCheck($parts, ':'); - $structure = array(); - - // And validate the group part of the name. - if (!$this->_validatePhrase($groupname)){ - $this->error = 'Group name did not validate.'; - return false; - } else { - // Don't include groups if we are not nesting - // them. This avoids returning invalid addresses. - if ($this->nestGroups) { - $structure = new stdClass; - $structure->groupname = $groupname; - } - } - - $address['address'] = ltrim(substr($address['address'], strlen($groupname . ':'))); - } - - // If a group then split on comma and put into an array. - // Otherwise, Just put the whole address in an array. - if ($is_group) { - while (strlen($address['address']) > 0) { - $parts = explode(',', $address['address']); - $addresses[] = $this->_splitCheck($parts, ','); - $address['address'] = trim(substr($address['address'], strlen(end($addresses) . ','))); - } - } else { - $addresses[] = $address['address']; - } - - // Trim the whitespace from all of the address strings. - array_map('trim', $addresses); - - // Validate each mailbox. - // Format could be one of: name - // geezer@domain.com - // geezer - // ... or any other format valid by RFC 822. - for ($i = 0; $i < count($addresses); $i++) { - if (!$this->validateMailbox($addresses[$i])) { - if (empty($this->error)) { - $this->error = 'Validation failed for: ' . $addresses[$i]; - } - return false; - } - } - - // Nested format - if ($this->nestGroups) { - if ($is_group) { - $structure->addresses = $addresses; - } else { - $structure = $addresses[0]; - } - - // Flat format - } else { - if ($is_group) { - $structure = array_merge($structure, $addresses); - } else { - $structure = $addresses; - } - } - - return $structure; - } - - /** - * Function to validate a phrase. - * - * @param string $phrase The phrase to check. - * @return boolean Success or failure. - */ - protected function _validatePhrase($phrase) - { - // Splits on one or more Tab or space. - $parts = preg_split('/[ \\x09]+/', $phrase, -1, PREG_SPLIT_NO_EMPTY); - - $phrase_parts = array(); - while (count($parts) > 0){ - $phrase_parts[] = $this->_splitCheck($parts, ' '); - for ($i = 0; $i < $this->index + 1; $i++) - array_shift($parts); - } - - foreach ($phrase_parts as $part) { - // If quoted string: - if (substr($part, 0, 1) == '"') { - if (!$this->_validateQuotedString($part)) { - return false; - } - continue; - } - - // Otherwise it's an atom: - if (!$this->_validateAtom($part)) return false; - } - - return true; - } - - /** - * Function to validate an atom which from rfc822 is: - * atom = 1* - * - * If validation ($this->validate) has been turned off, then - * validateAtom() doesn't actually check anything. This is so that you - * can split a list of addresses up before encoding personal names - * (umlauts, etc.), for example. - * - * @param string $atom The string to check. - * @return boolean Success or failure. - */ - protected function _validateAtom($atom) - { - if (!$this->validate) { - // Validation has been turned off; assume the atom is okay. - return true; - } - - // Check for any char from ASCII 0 - ASCII 127 - if (!preg_match('/^[\\x00-\\x7E]+$/i', $atom, $matches)) { - return false; - } - - // Check for specials: - if (preg_match('/[][()<>@,;\\:". ]/', $atom)) { - return false; - } - - // Check for control characters (ASCII 0-31): - if (preg_match('/[\\x00-\\x1F]+/', $atom)) { - return false; - } - - return true; - } - - /** - * Function to validate quoted string, which is: - * quoted-string = <"> *(qtext/quoted-pair) <"> - * - * @param string $qstring The string to check - * @return boolean Success or failure. - */ - protected function _validateQuotedString($qstring) - { - // Leading and trailing " - $qstring = substr($qstring, 1, -1); - - // Perform check, removing quoted characters first. - return !preg_match('/[\x0D\\\\"]/', preg_replace('/\\\\./', '', $qstring)); - } - - /** - * Function to validate a mailbox, which is: - * mailbox = addr-spec ; simple address - * / phrase route-addr ; name and route-addr - * - * @param string &$mailbox The string to check. - * @return boolean Success or failure. - */ - public function validateMailbox(&$mailbox) - { - // A couple of defaults. - $phrase = ''; - $comment = ''; - $comments = array(); - - // Catch any RFC822 comments and store them separately. - $_mailbox = $mailbox; - while (strlen(trim($_mailbox)) > 0) { - $parts = explode('(', $_mailbox); - $before_comment = $this->_splitCheck($parts, '('); - if ($before_comment != $_mailbox) { - // First char should be a (. - $comment = substr(str_replace($before_comment, '', $_mailbox), 1); - $parts = explode(')', $comment); - $comment = $this->_splitCheck($parts, ')'); - $comments[] = $comment; - - // +2 is for the brackets - $_mailbox = substr($_mailbox, strpos($_mailbox, '('.$comment)+strlen($comment)+2); - } else { - break; - } - } - - foreach ($comments as $comment) { - $mailbox = str_replace("($comment)", '', $mailbox); - } - - $mailbox = trim($mailbox); - - // Check for name + route-addr - if (substr($mailbox, -1) == '>' && substr($mailbox, 0, 1) != '<') { - $parts = explode('<', $mailbox); - $name = $this->_splitCheck($parts, '<'); - - $phrase = trim($name); - $route_addr = trim(substr($mailbox, strlen($name.'<'), -1)); - - //z-push fix for umlauts and other special chars - if (substr($phrase, 0, 1) != '"' && substr($phrase, -1) != '"') { - $phrase = '"'.$phrase.'"'; - } - - if ($this->_validatePhrase($phrase) === false || ($route_addr = $this->_validateRouteAddr($route_addr)) === false) { - return false; - } - - // Only got addr-spec - } else { - // First snip angle brackets if present. - if (substr($mailbox, 0, 1) == '<' && substr($mailbox, -1) == '>') { - $addr_spec = substr($mailbox, 1, -1); - } else { - $addr_spec = $mailbox; - } - - if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { - return false; - } - } - - // Construct the object that will be returned. - $mbox = new stdClass(); - - // Add the phrase (even if empty) and comments - $mbox->personal = $phrase; - $mbox->comment = isset($comments) ? $comments : array(); - - if (isset($route_addr)) { - $mbox->mailbox = $route_addr['local_part']; - $mbox->host = $route_addr['domain']; - $route_addr['adl'] !== '' ? $mbox->adl = $route_addr['adl'] : ''; - } else { - $mbox->mailbox = $addr_spec['local_part']; - $mbox->host = $addr_spec['domain']; - } - - $mailbox = $mbox; - return true; - } - - /** - * This function validates a route-addr which is: - * route-addr = "<" [route] addr-spec ">" - * - * Angle brackets have already been removed at the point of - * getting to this function. - * - * @param string $route_addr The string to check. - * @return mixed False on failure, or an array containing validated address/route information on success. - */ - protected function _validateRouteAddr($route_addr) - { - // Check for colon. - if (strpos($route_addr, ':') !== false) { - $parts = explode(':', $route_addr); - $route = $this->_splitCheck($parts, ':'); - } else { - $route = $route_addr; - } - - // If $route is same as $route_addr then the colon was in - // quotes or brackets or, of course, non existent. - if ($route === $route_addr){ - unset($route); - $addr_spec = $route_addr; - if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { - return false; - } - } else { - // Validate route part. - if (($route = $this->_validateRoute($route)) === false) { - return false; - } - - $addr_spec = substr($route_addr, strlen($route . ':')); - - // Validate addr-spec part. - if (($addr_spec = $this->_validateAddrSpec($addr_spec)) === false) { - return false; - } - } - - if (isset($route)) { - $return['adl'] = $route; - } else { - $return['adl'] = ''; - } - - $return = array_merge($return, $addr_spec); - return $return; - } - - /** - * Function to validate a route, which is: - * route = 1#("@" domain) ":" - * - * @param string $route The string to check. - * @return mixed False on failure, or the validated $route on success. - */ - protected function _validateRoute($route) - { - // Split on comma. - $domains = explode(',', trim($route)); - - foreach ($domains as $domain) { - $domain = str_replace('@', '', trim($domain)); - if (!$this->_validateDomain($domain)) return false; - } - - return $route; - } - - /** - * Function to validate a domain, though this is not quite what - * you expect of a strict internet domain. - * - * domain = sub-domain *("." sub-domain) - * - * @param string $domain The string to check. - * @return mixed False on failure, or the validated domain on success. - */ - protected function _validateDomain($domain) - { - // Note the different use of $subdomains and $sub_domains - $subdomains = explode('.', $domain); - - while (count($subdomains) > 0) { - $sub_domains[] = $this->_splitCheck($subdomains, '.'); - for ($i = 0; $i < $this->index + 1; $i++) - array_shift($subdomains); - } - - foreach ($sub_domains as $sub_domain) { - if (!$this->_validateSubdomain(trim($sub_domain))) - return false; - } - - // Managed to get here, so return input. - return $domain; - } - - /** - * Function to validate a subdomain: - * subdomain = domain-ref / domain-literal - * - * @param string $subdomain The string to check. - * @return boolean Success or failure. - */ - protected function _validateSubdomain($subdomain) - { - if (preg_match('|^\[(.*)]$|', $subdomain, $arr)){ - if (!$this->_validateDliteral($arr[1])) return false; - } else { - if (!$this->_validateAtom($subdomain)) return false; - } - - // Got here, so return successful. - return true; - } - - /** - * Function to validate a domain literal: - * domain-literal = "[" *(dtext / quoted-pair) "]" - * - * @param string $dliteral The string to check. - * @return boolean Success or failure. - */ - protected function _validateDliteral($dliteral) - { - return !preg_match('/(.)[][\x0D\\\\]/', $dliteral, $matches) && $matches[1] != '\\'; - } - - /** - * Function to validate an addr-spec. - * - * addr-spec = local-part "@" domain - * - * @param string $addr_spec The string to check. - * @return mixed False on failure, or the validated addr-spec on success. - */ - protected function _validateAddrSpec($addr_spec) - { - $addr_spec = trim($addr_spec); - - // Split on @ sign if there is one. - if (strpos($addr_spec, '@') !== false) { - $parts = explode('@', $addr_spec); - $local_part = $this->_splitCheck($parts, '@'); - $domain = substr($addr_spec, strlen($local_part . '@')); - - // No @ sign so assume the default domain. - } else { - $local_part = $addr_spec; - $domain = $this->default_domain; - } - - if (($local_part = $this->_validateLocalPart($local_part)) === false) return false; - if (($domain = $this->_validateDomain($domain)) === false) return false; - - // Got here so return successful. - return array('local_part' => $local_part, 'domain' => $domain); - } - - /** - * Function to validate the local part of an address: - * local-part = word *("." word) - * - * @param string $local_part - * @return mixed False on failure, or the validated local part on success. - */ - protected function _validateLocalPart($local_part) - { - $parts = explode('.', $local_part); - $words = array(); - - // Split the local_part into words. - while (count($parts) > 0) { - $words[] = $this->_splitCheck($parts, '.'); - for ($i = 0; $i < $this->index + 1; $i++) { - array_shift($parts); - } - } - - // Validate each word. - foreach ($words as $word) { - // word cannot be empty (#17317) - if ($word === '') { - return false; - } - // If this word contains an unquoted space, it is invalid. (6.2.4) - if (strpos($word, ' ') && $word[0] !== '"') - { - return false; - } - - if ($this->_validatePhrase(trim($word)) === false) return false; - } - - // Managed to get here, so return the input. - return $local_part; - } - - /** - * Returns an approximate count of how many addresses are in the - * given string. This is APPROXIMATE as it only splits based on a - * comma which has no preceding backslash. Could be useful as - * large amounts of addresses will end up producing *large* - * structures when used with parseAddressList(). - * - * @param string $data Addresses to count - * @return int Approximate count - */ - function approximateCount($data) - { - return count(preg_split('/(?@. This can be sufficient for most - * people. Optional stricter mode can be utilised which restricts - * mailbox characters allowed to alphanumeric, full stop, hyphen - * and underscore. - * - * @param string $data Address to check - * @param boolean $strict Optional stricter mode - * @return mixed False if it fails, an indexed array - * username/domain if it matches - */ - function isValidInetAddress($data, $strict = false) - { - $regex = $strict ? '/^([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i' : '/^([*+!.&#$|\'\\%\/0-9a-z^_`{}=?~:-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})$/i'; - if (preg_match($regex, trim($data), $matches)) { - return array($matches[1], $matches[2]); - } else { - return false; - } - } - /** - * Z-Push helper for error logging - * removing PEAR dependency - * - * @param string debug message - * @return boolean always false as there was an error - * @access private - */ - function raiseError($message) { - ZLog::Write(LOGLEVEL_ERROR, "z_RFC822 error: ". $message); - return false; - } -} \ No newline at end of file diff --git a/sources/include/z_RTF.php b/sources/include/z_RTF.php deleted file mode 100644 index 9330717..0000000 --- a/sources/include/z_RTF.php +++ /dev/null @@ -1,704 +0,0 @@ - - http://josefine.ben.tuwien.ac.at/~mfischer/ - - Latest versions of this class can always be found at - http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/rtfclass.phps - Testing suite is available at - http://josefine.ben.tuwien.ac.at/~mfischer/developing/php/rtf/ - - License: GPLv2 - - Specification: - http://msdn.microsoft.com/library/default.asp?URL=/library/specs/rtfspec.htm - - General Notes: - ============== - Unknown or unspupported control symbols are silently gnored - - Group stacking is still not supported :( - group stack logic implemented; however not really used yet - ===================================================================================================== - - It was modified by me (Andreas Brodowski) to allow compressed RTF being uncompressed by code I ported from - Java to PHP and adapted according the needs of Z-Push. - - Currently it is being used to detect empty RTF Streams from Nokia Phones in MfE Clients - - It needs to be used by other backend writers that needs to have notes in calendar, appointment or tasks - objects to be written to their databases since devices send them usually in RTF Format... With Zarafa - you can write them directly to DB and Zarafa is doing the conversion job. Other Groupware systems usually - don't have this possibility... - -*/ - -class rtf { - var $LZRTF_HDR_DATA = "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx"; - var $LZRTF_HDR_LEN = 207; - var $CRC32_TABLE = array( 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, - 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, - 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, - 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, - 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, - 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, - 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, - 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, - 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, - 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, - 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, - 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, - 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, - 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, - 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, - 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, - 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, - 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, - 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, - 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, - 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, - 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, - 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, - 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, - 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, - 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, - 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, - 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, - 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, - 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, - 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, - 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, - ); - - var $rtf; // rtf core stream - var $rtf_len; // length in characters of the stream (get performace due avoiding calling strlen everytime) - var $err = array(); // array of error message, no entities on no error - - var $wantXML = false; // convert to XML - var $wantHTML = false; // convert to HTML - var $wantASCII = false; // convert to HTML - - // the only variable which should be accessed from the outside - var $out; // output data stream (depends on which $wantXXXXX is set to true - var $outstyles; // htmlified styles (generated after parsing if wantHTML - var $styles; // if wantHTML, stylesheet definitions are put in here - - // internal parser variables -------------------------------- - // control word variables - var $cword; // holds the current (or last) control word, depending on $cw - var $cw; // are we currently parsing a control word ? - var $cfirst; // could this be the first character ? so watch out for control symbols - - var $flags = array(); // parser flags - - var $queue; // every character which is no sepcial char, not belongs to a control word/symbol; is generally considered being 'plain' - - var $stack = array(); // group stack - - /* keywords which don't follw the specification (used by Word '97 - 2000) */ - // not yet used - var $control_exception = array( - "clFitText", - "clftsWidth(-?[0-9]+)?", - "clNoWrap(-?[0-9]+)?", - "clwWidth(-?[0-9]+)?", - "tdfrmtxtBottom(-?[0-9]+)?", - "tdfrmtxtLeft(-?[0-9]+)?", - "tdfrmtxtRight(-?[0-9]+)?", - "tdfrmtxtTop(-?[0-9]+)?", - "trftsWidthA(-?[0-9]+)?", - "trftsWidthB(-?[0-9]+)?", - "trftsWidth(-?[0-9]+)?", - "trwWithA(-?[0-9]+)?", - "trwWithB(-?[0-9]+)?", - "trwWith(-?[0-9]+)?", - "spectspecifygen(-?[0-9]+)?", - ); - - var $charset_table = array( - "0" => "ANSI", - "1" => "Default", - "2" => "Symbol", - "77" => "Mac", - "128" => "Shift Jis", - "129" => "Hangul", - "130" => "Johab", - "134" => "GB2312", - "136" => "Big5", - "161" => "Greek", - "162" => "Turkish", - "163" => "Vietnamese", - "177" => "Hebrew", - "178" => "Arabic", - "179" => "Arabic Traditional", - "180" => "Arabic user", - "181" => "Hebrew user", - "186" => "Baltic", - "204" => "Russian", - "222" => "Thai", - "238" => "Eastern European", - "255" => "PC 437", - "255" => "OEM", - ); - - /* note: the only conversion table used */ - var $fontmodifier_table = array( - "bold" => "b", - "italic" => "i", - "underlined" => "u", - "strikethru" => "strike", - ); - - - function rtf() { - $this->rtf_len = 0; - $this->rtf = ''; - - $this->out = ''; - } - - // loadrtf - load the raw rtf data to be converted by this class - // data = the raw rtf - function loadrtf($data) { - if (($this->rtf = $this->uncompress($data))) { - $this->rtf_len = strlen($this->rtf); - }; - if($this->rtf_len == 0) { - debugLog("No data in stream found"); - return false; - }; - return true; - } - - function output($typ) { - switch($typ) { - case "ascii": $this->wantASCII = true; break; - case "xml": $this->wantXML = true; break; - case "html": $this->wantHTML = true; break; - default: break; - } - } - - // uncompress - uncompress compressed rtf data - // src = the compressed raw rtf in LZRTF format - function uncompress($src) { - $header = unpack("LcSize/LuSize/Lmagic/Lcrc32",substr($src,0,16)); - $in = 16; - if ($header['cSize'] != strlen($src)-4) { - debugLog("Stream too short"); - return false; - } - - if ($header['crc32'] != $this->LZRTFCalcCRC32($src,16,(($header['cSize']+4))-16)) { - debugLog("CRC MISMATCH"); - return false; - } - - if ($header['magic'] == 0x414c454d) { // uncompressed RTF - return as is. - $dest = substr($src,$in,$header['uSize']); - } else if ($header['magic'] == 0x75465a4c) { // compressed RTF - uncompress. - $dst = $this->LZRTF_HDR_DATA; - $out = $this->LZRTF_HDR_LEN; - $oblen = $this->LZRTF_HDR_LEN + $header['uSize']; - $flagCount = 0; - $flags = 0; - while ($out<$oblen) { - $flags = ($flagCount++ % 8 == 0) ? ord($src{$in++}) : $flags >> 1; - if (($flags & 1) == 1) { - $offset = ord($src{$in++}); - $length = ord($src{$in++}); - $offset = ($offset << 4) | ($length >> 4); - $length = ($length & 0xF) + 2; - $offset = (int)($out / 4096) * 4096 + $offset; - if ($offset >= $out) $offset -= 4096; - $end = $offset + $length; - while ($offset < $end) { - $dst{$out++} = $dst{$offset++}; - }; - } else { - $dst{$out++} = $src{$in++}; - } - } - $src = $dst; - $dest = substr($src,$this->LZRTF_HDR_LEN,$header['uSize']); - } else { // unknown magic - returfn false (please report if this ever happens) - debugLog("Unknown Magic"); - return false; - } - - return $dest; - } - - // LZRTFCalcCRC32 - calculates the CRC32 of the LZRTF data part - // buf = the whole rtf data part - // off = start point of crc calculation - // len = length of data to calculate CRC for - // function is necessary since in RTF there is no XOR 0xffffffff being done (said to be 0x00 unsafe CRC32 calculation - function LZRTFCalcCRC32($buf, $off, $len) { - $c=0; - $end = $off + $len; - for($i=$off;$i < $end;$i++) { - $c=$this->CRC32_TABLE[($c ^ ord($buf{$i})) & 0xFF] ^ (($c >> 8) & 0x00ffffff); - } - return $c; - } - - function parserInit() { /* Default values according to the specs */ - $this->flags = array( - "fontsize" => 24, - "beginparagraph" => true, - ); - } - - function parseControl($control, $parameter) { - switch ($control) { - case "fonttbl": // font table definition start - $this->flags["fonttbl"] = true; // signal fonttable control words they are allowed to behave as expected - break; - case "f": // define or set font - if($this->flags["fonttbl"]) { // if its set, the fonttable definition is written to; else its read from - $this->flags["fonttbl_current_write"] = $parameter; - } else { - $this->flags["fonttbl_current_read"] = $parameter; - } - break; - case "fcharset": // this is for preparing flushQueue; it then moves the Queue to $this->fonttable .. instead to formatted output - $this->flags["fonttbl_want_fcharset"] = $parameter; - break; - case "fs": // sets the current fontsize; is used by stylesheets (which are therefore generated on the fly - $this->flags["fontsize"] = $parameter; - break; - - case "qc": // handle center alignment - $this->flags["alignment"] = "center"; - break; - case "qr": // handle right alignment - $this->flags["alignment"] = "right"; - break; - - case "pard": // reset paragraph settings (only alignment) - $this->flags["alignment"] = ""; - break; - case "par": // define new paragraph (for now, thats a simple break in html) begin new line - $this->flags["beginparagraph"] = true; - if($this->wantHTML) { - $this->out .= ""; - } - if($this->wantASCII) { - $this->out .= "\n"; - } - break; - case "bnone": // bold - $parameter = "0"; - case "b": - // haven'y yet figured out WHY I need a (string)-cast here ... hm - if((string)$parameter == "0") - $this->flags["bold"] = false; - else - $this->flags["bold"] = true; - break; - case "ulnone": // underlined - $parameter = "0"; - case "ul": - if((string)$parameter == "0") - $this->flags["underlined"] = false; - else - $this->flags["underlined"] = true; - break; - case "inone": // italic - $parameter = "0"; - case "i": - if((string)$parameter == "0") - $this->flags["italic"] = false; - else - $this->flags["italic"] = true; - break; - case "strikenone": // strikethru - $parameter = "0"; - case "strike": - if((string)$parameter == "0") - $this->flags["strikethru"] = false; - else - $this->flags["strikethru"] = true; - break; - case "plain": // reset all font modifiers and fontsize to 12 - $this->flags["bold"] = false; - $this->flags["italic"] = false; - $this->flags["underlined"] = false; - $this->flags["strikethru"] = false; - $this->flags["fontsize"] = 12; - - $this->flags["subscription"] = false; - $this->flags["superscription"] = false; - break; - case "subnone": // subscription - $parameter = "0"; - case "sub": - if((string)$parameter == "0") - $this->flags["subscription"] = false; - else - $this->flags["subscription"] = true; - break; - case "supernone": // superscription - $parameter = "0"; - case "super": - if((string)$parameter == "0") - $this->flags["superscription"] = false; - else - $this->flags["superscription"] = true; - break; - } - } - - /* - Dispatch the control word to the output stream - */ - - function flushControl() { - if(preg_match("/^([A-Za-z]+)(-?[0-9]*) ?$/", $this->cword, $match)) { - $this->parseControl($match[1], $match[2]); - if($this->wantXML) { - $this->out.=" 0) - $this->out.=" param=\"".$match[2]."\""; - $this->out.="/>"; - } - } - } - - /* - If output stream supports comments, dispatch it - */ - - function flushComment($comment) { - if($this->wantXML || $this->wantHTML) { - $this->out.=""; - } - } - - /* - Dispatch start/end of logical rtf groups (not every output type needs it; merely debugging purpose) - */ - - function flushGroup($state) { - if($state == "open") { /* push onto the stack */ - array_push($this->stack, $this->flags); - - if($this->wantXML) - $this->out.=""; - } - if($state == "close") { /* pop from the stack */ - $this->last_flags = $this->flags; - $this->flags = array_pop($this->stack); - - $this->flags["fonttbl_current_write"] = ""; // on group close, no more fontdefinition will be written to this id - // this is not really the right way to do it ! - // of course a '}' not necessarily donates a fonttable end; a fonttable - // group at least *can* contain sub-groups - // therefore an stacked approach is heavily needed - $this->flags["fonttbl"] = false; // no matter what you do, if a group closes, its fonttbl definition is closed too - - if($this->wantXML) - $this->out.=""; - } - } - - function flushHead() { - if($this->wantXML) - $this->out.=""; - } - - function flushBottom() { - if($this->wantXML) - $this->out.=""; - } - - function checkHtmlSpanContent($command) { - reset($this->fontmodifier_table); - while(list($rtf, $html) = each($this->fontmodifier_table)) { - if($this->flags[$rtf] == true) { - if($command == "start") - $this->out .= "<".$html.">"; - else - $this->out .= ""; - } - } - } - - /* - flush text in queue - */ - function flushQueue() { - if(strlen($this->queue)) { - // processing logic - if (isset($this->flags["fonttbl_want_fcharset"]) && - preg_match("/^[0-9]+$/", $this->flags["fonttbl_want_fcharset"])) { - $this->fonttable[$this->flags["fonttbl_want_fcharset"]]["charset"] = $this->queue; - $this->flags["fonttbl_want_fcharset"] = ""; - $this->queue = ""; - } - - // output logic - if (strlen($this->queue)) { - /* - Everything which passes this is (or, at leat, *should*) be only outputted plaintext - Thats why we can safely add the css-stylesheet when using wantHTML - */ - if($this->wantXML) - $this->out.= "".$this->queue.""; - else if($this->wantHTML) { - // only output html if a valid (for now, just numeric;) fonttable is given - if (!isset($this->flags["fonttbl_current_read"])) $this->flags["fonttbl_current_read"] = ""; - if(preg_match("/^[0-9]+$/", $this->flags["fonttbl_current_read"])) { - if($this->flags["beginparagraph"] == true) { - $this->flags["beginparagraph"] = false; - $this->out .= "
flags["alignment"]) { - case "right": - $this->out .= "right"; - break; - case "center": - $this->out .= "center"; - break; - case "left": - default: - $this->out .= "left"; - } - $this->out .= "\">"; - } - - /* define new style for that span */ - $this->styles["f".$this->flags["fonttbl_current_read"]."s".$this->flags["fontsize"]] = "font-family:".$this->fonttable[$this->flags["fonttbl_current_read"]]["charset"]." font-size:".$this->flags["fontsize"].";"; - /* write span start */ - $this->out .= "flags["fonttbl_current_read"]."s".$this->flags["fontsize"]."\">"; - - /* check if the span content has a modifier */ - $this->checkHtmlSpanContent("start"); - /* write span content */ - $this->out .= $this->queue; - /* close modifiers */ - $this->checkHtmlSpanContent("stop"); - /* close span */ - ""; - } - } - $this->queue = ""; - } - } - } - - /* - handle special charactes like \'ef - */ - - function flushSpecial($special) { - if(strlen($special) == 2) { - if($this->wantASCII) - $this->out .= chr(hexdec('0x'.$special)); - else if($this->wantXML) - $this->out .= ""; - else if($this->wantHTML){ - $this->out .= ""; - switch($special) { - case "c1": $this->out .= "Á"; break; - case "e1": $this->out .= "á"; break; - case "c0": $this->out .= "À"; break; - case "e0": $this->out .= "à"; break; - case "c9": $this->out .= "É"; break; - case "e9": $this->out .= "é"; break; - case "c8": $this->out .= "È"; break; - case "e8": $this->out .= "è"; break; - case "cd": $this->out .= "Í"; break; - case "ed": $this->out .= "í"; break; - case "cc": $this->out .= "Ì"; break; - case "ec": $this->out .= "ì"; break; - case "d3": $this->out .= "Ó"; break; - case "f3": $this->out .= "ó"; break; - case "d2": $this->out .= "Ò"; break; - case "f2": $this->out .= "ò"; break; - case "da": $this->out .= "Ú"; break; - case "fa": $this->out .= "ú"; break; - case "d9": $this->out .= "Ù"; break; - case "f9": $this->out .= "ù"; break; - case "80": $this->out .= "€"; break; - case "d1": $this->out .= "Ñ"; break; - case "f1": $this->out .= "ñ"; break; - case "c7": $this->out .= "Ç"; break; - case "e7": $this->out .= "ç"; break; - case "dc": $this->out .= "Ü"; break; - case "fc": $this->out .= "ü"; break; - case "bf": $this->out .= "¿"; break; - case "a1": $this->out .= "¡"; break; - case "b7": $this->out .= "·"; break; - case "a9": $this->out .= "©"; break; - case "ae": $this->out .= "®"; break; - case "ba": $this->out .= "º"; break; - case "aa": $this->out .= "ª"; break; - case "b2": $this->out .= "²"; break; - case "b3": $this->out .= "³"; break; - } - } - } - } - - /* - Output errors at end - */ - function flushErrors() { - if(count($this->err) > 0) { - if($this->wantXML) { - $this->out .= ""; - while(list($num,$value) = each($this->err)) { - $this->out .= "".$value.""; - } - $this->out .= ""; - } - } - } - - function makeStyles() { - $this->outstyles = "\n"; - } - - function parse() { - - $this->parserInit(); - - $i = 0; - $this->cw= false; // flag if control word is currently parsed - $this->cfirst = false; // first control character ? - $this->cword = ""; // last or current control word (depends on $this->cw - - $this->queue = ""; // plain text data found during parsing - - $this->flushHead(); - - while($i < $this->rtf_len) { - switch($this->rtf[$i]) { - case "{": - if($this->cw) { - $this->flushControl(); - $this->cw = false; - $this->cfirst = false; - } else - $this->flushQueue(); - - $this->flushGroup("open"); - break; - case "}": - if($this->cw) { - $this->flushControl(); - $this->cw = false; - $this->cfirst = false; - } else - $this->flushQueue(); - - $this->flushGroup("close"); - break; - case "\\": - if($this->cfirst) { // catches '\\' - $this->queue .= "\\"; // replaced single quotes - $this->cfirst = false; - $this->cw = false; - break; - } - if($this->cw) { - $this->flushControl(); - } else - $this->flushQueue(); - $this->cw = true; - $this->cfirst = true; - $this->cword = ""; - break; - default: - if((ord($this->rtf[$i]) == 10) || (ord($this->rtf[$i]) == 13)) break; // eat line breaks - if($this->cw) { // active control word ? - /* - Watch the RE: there's an optional space at the end which IS part of - the control word (but actually its ignored by flushControl) - */ - if(preg_match("/^[a-zA-Z0-9-]?$/", $this->rtf[$i])) { // continue parsing - $this->cword .= $this->rtf[$i]; - $this->cfirst = false; - } else { - /* - Control word could be a 'control symbol', like \~ or \* etc. - */ - $specialmatch = false; - if($this->cfirst) { - if($this->rtf[$i] == '\'') { // expect to get some special chars - $this->flushQueue(); - $this->flushSpecial($this->rtf[$i+1].$this->rtf[$i+2]); - $i+=2; - $specialmatch = true; - $this->cw = false; - $this->cfirst = false; - $this->cword = ""; - } else - if(preg_match("/^[{}\*]$/", $this->rtf[$i])) { - $this->flushComment("control symbols not yet handled"); - $specialmatch = true; - } - $this->cfirst = false; - } else { - if($this->rtf[$i] == ' ') { // space delimtes control words, so just discard it and flush the controlword - $this->cw = false; - $this->flushControl(); - break; - } - } - if(!$specialmatch) { - $this->flushControl(); - $this->cw = false; - $this->cfirst = false; - /* - The current character is a delimeter, but is NOT - part of the control word so we hop one step back - in the stream and process it again - */ - $i--; - } - } - } else { - // < and > need translation before putting into queue when XML or HTML is wanted - if(($this->wantHTML) || ($this->wantXML)) { - switch($this->rtf[$i]) { - case "<": - $this->queue .= "<"; - break; - case ">": - $this->queue .= ">"; - break; - default: - $this->queue .= $this->rtf[$i]; - break; - } - } else - $this->queue .= $this->rtf[$i]; - } - - } - $i++; - } - $this->flushQueue(); - $this->flushErrors(); - $this->flushBottom(); - - if($this->wantHTML) { - $this->makeStyles(); - } - } -} \ No newline at end of file diff --git a/sources/include/z_caldav.php b/sources/include/z_caldav.php deleted file mode 100644 index 234da81..0000000 --- a/sources/include/z_caldav.php +++ /dev/null @@ -1,1019 +0,0 @@ - -* but using cURL instead of home-brew request construction. cURL code re-used -* from carddav.php by Jean-Louis Dupond. Additional bugfixes to -* caldav-client-v2.php by xbgmsharp . -* -* Copyright Andrew McMillan (original caldav-client-v2.php), Jean-Louis Dupond (cURL code), xbgmsharp (bugfixes) -* Copyright Thorsten Köster -* License GNU LGPL version 3 or later (http://www.gnu.org/licenses/lgpl-3.0.txt) -*/ - -require_once('XMLDocument.php'); - -/** -* A class for holding basic calendar information -*/ -class CalendarInfo { - public $url; - public $displayname; - public $getctag; - public $id; - - function __construct( $url, $displayname = null, $getctag = null, $id = null ) { - $this->url = $url; - $this->displayname = $displayname; - $this->getctag = $getctag; - $this->id = $id; - } - - function __toString() { - return( '(URL: '.$this->url.' Ctag: '.$this->getctag.' Displayname: '.$this->displayname .')'. "\n" ); - } -} - - -/** -* A class for accessing DAViCal via CalDAV, as a client -* -* @package awl -*/ -class CalDAVClient { - /** - * Server, username, password, calendar - * - * @var string - */ - protected $server, $base_url, $user, $pass, $auth; - - /** - * The principal-URL we're using - */ - protected $principal_url; - - /** - * The calendar-URL we're using - */ - protected $calendar_url; - - /** - * The calendar-home-set we're using - */ - protected $calendar_home_set; - - /** - * The calendar_urls we have discovered - */ - protected $calendar_urls; - - /** - * Construct URL - */ - protected $url; - - /** - * The useragent which is send to the caldav server - * - * @var string - */ - const USERAGENT = 'ModifiedDAViCalClient'; - - protected $headers = array(); - protected $xmlResponse = ""; // xml received - protected $httpResponseCode = 0; // http response code - protected $httpResponseHeaders = ""; - protected $httpResponseBody = ""; - - protected $parser; // our XML parser object - - /** - * CardDAV server connection (curl handle) - * - * @var resource - */ - private $curl = false; - - private $synctoken = array(); - - /** - * Constructor, initialises the class - * - * @param string $caldav_url The URL for the calendar server - * @param string $user The name of the user logging in - * @param string $pass The password for that user - */ - function __construct( $caldav_url, $user, $pass ) { - $this->url = $caldav_url; - $this->user = $user; - $this->pass = $pass; - $this->auth = $user . ':' . $pass; - $this->headers = array(); - - $parsed_url = parse_url($caldav_url); - if ($parsed_url === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("BackendCalDAV->caldav_backend(): Couldn't parse URL: %s", $caldav_url)); - return; - } - - $this->server = $parsed_url['scheme'] . '://' . $parsed_url['host'] . ':' . $parsed_url['port']; - $this->base_url = $parsed_url['path']; - //ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCalDAV->caldav_backend(): base_url '%s'", $this->base_url)); - //$this->base_url .= !empty($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; - //$this->base_url .= !empty($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; - - if (substr($this->base_url, -1) !== '/') { - $this->base_url = $this->base_url . '/'; - } - } - - /** - * Checks if the CalDAV server is reachable - * - * @return boolean - */ - public function CheckConnection() { - $result = $this->DoRequest($this->url, 'OPTIONS'); - - switch ($this->httpResponseCode) { - case 200: - case 207: - case 401: - $status = true; - break; - default: - $status = false; - } - - return $status; - } - - /** - * Disconnect curl connection - * - */ - public function Disconnect() { - if ($this->curl !== false) { - curl_close($this->curl); - $this->curl = false; - } - } - - - /** - * Adds an If-Match or If-None-Match header - * - * @param bool $match to Match or Not to Match, that is the question! - * @param string $etag The etag to match / not match against. - */ - function SetMatch( $match, $etag = '*' ) { - $this->headers['match'] = sprintf( "%s-Match: \"%s\"", ($match ? "If" : "If-None"), trim($etag,'"')); - } - - /** - * Add a Depth: header. Valid values are 0, 1 or infinity - * - * @param int $depth The depth, default to infinity - */ - function SetDepth( $depth = '0' ) { - $this->headers['depth'] = 'Depth: '. ($depth == '1' ? "1" : ($depth == 'infinity' ? $depth : "0") ); - } - - /** - * Set the calendar_url we will be using for a while. - * - * @param string $url The calendar_url - */ - function SetCalendar( $url ) { - $this->calendar_url = $url; - } - - /** - * Split response into httpResponse and xmlResponse - * - * @param string Response from server - */ - function ParseResponse( $response ) { - $pos = strpos($response, 'xmlResponse = trim(substr($response, $pos)); - $this->xmlResponse = preg_replace('{>[^>]*$}s', '>',$this->xmlResponse ); - $parser = xml_parser_create_ns('UTF-8'); - xml_parser_set_option ( $parser, XML_OPTION_SKIP_WHITE, 1 ); - xml_parser_set_option ( $parser, XML_OPTION_CASE_FOLDING, 0 ); - - if ( xml_parse_into_struct( $parser, $this->xmlResponse, $this->xmlnodes, $this->xmltags ) === 0 ) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("XML parsing error: %s - %s", xml_get_error_code($parser), xml_error_string(xml_get_error_code($parser)))); -// debug_print_backtrace(); -// echo "\nNodes array............................................................\n"; print_r( $this->xmlnodes ); -// echo "\nTags array............................................................\n"; print_r( $this->xmltags ); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("XML Reponse:\n%s\n", $this->xmlResponse)); - } - - xml_parser_free($parser); - } - } - - - public function curl_init() { - if ($this->curl === false) { - $this->curl = curl_init(); - curl_setopt($this->curl, CURLOPT_HEADER, true); - curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->curl, CURLOPT_USERAGENT, self::USERAGENT); - - if ($this->auth !== null) { - curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - curl_setopt($this->curl, CURLOPT_USERPWD, $this->auth); - } - } - } - - - /** - * Send a request to the server - * - * @param string $url The URL to make the request to - * - * @return string The content of the response from the server - */ - function DoRequest($url, $method, $content = null, $content_type = "text/plain") { - $this->curl_init(); - - if ( !isset($url) ) $url = $this->base_url; - $url = preg_replace('{^https?://[^/]+}', '', $url); - $url = $this->server . $url; - - curl_setopt($this->curl, CURLOPT_URL, $url); - curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method); - curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, 30); // 30 seconds it's already too big - - if ($content !== null) - { - curl_setopt($this->curl, CURLOPT_POST, true); - curl_setopt($this->curl, CURLOPT_POSTFIELDS, $content); - } - else - { - curl_setopt($this->curl, CURLOPT_POST, false); - curl_setopt($this->curl, CURLOPT_POSTFIELDS, null); - } - - $headers = array(); - $headers['content-type'] = 'Content-type: ' . $content_type; - foreach( $this->headers as $ii => $head ) { - $headers[$ii] = $head; - } - curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers); - - $this->xmlResponse = ''; - -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request:\n%s\n", $content)); - $response = curl_exec($this->curl); -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("Reponse:\n%s\n", $response)); - $header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE); - $this->httpResponseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); - $this->httpResponseHeaders = trim(substr($response, 0, $header_size)); - $this->httpResponseBody = substr($response, $header_size); - - $this->headers = array(); // reset the headers array for our next request - $this->ParseResponse($this->httpResponseBody); - return $response; - } - - - /** - * Send an OPTIONS request to the server - * - * @param string $url The URL to make the request to - * - * @return array The allowed options - */ - function DoOptionsRequest( $url = null ) { - $headers = $this->DoRequest($url === null ? $this->url : $url, "OPTIONS"); - $options_header = preg_replace( '/^.*Allow: ([a-z, ]+)\r?\n.*/is', '$1', $headers ); - $options = array_flip( preg_split( '/[, ]+/', $options_header )); - return $options; - } - - - /** - * Send an XML request to the server (e.g. PROPFIND, REPORT, MKCALENDAR) - * - * @param string $method The method (PROPFIND, REPORT, etc) to use with the request - * @param string $xml The XML to send along with the request - * @param string $url The URL to make the request to - * - * @return array An array of the allowed methods - */ - function DoXMLRequest( $request_method, $xml, $url = null ) { - return $this->DoRequest($url, $request_method, $xml, "text/xml"); - } - - - /** - * Get a single item from the server. - * - * @param string $url The URL to GET - */ - function DoGETRequest( $url ) { - return $this->DoRequest($url, "GET"); - } - - - /** - * Get the HEAD of a single item from the server. - * - * @param string $url The URL to HEAD - */ - function DoHEADRequest( $url ) { - return $this->DoRequest($url, "HEAD"); - } - - - /** - * PUT a text/icalendar resource, returning the etag - * - * @param string $url The URL to make the request to - * @param string $icalendar The iCalendar resource to send to the server - * @param string $etag The etag of an existing resource to be overwritten, or '*' for a new resource. - * - * @return string The content of the response from the server - */ - function DoPUTRequest( $url, $icalendar, $etag = null ) { - if ( $etag != null ) { - $this->SetMatch( ($etag != '*'), $etag ); - } - $this->DoRequest($url, "PUT", $icalendar, 'text/calendar; encoding="utf-8"'); - - $etag = null; - if ( preg_match( '{^ETag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) { - $etag = $matches[1]; - } - if ( !isset($etag) || $etag == '' ) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("No etag in:\n%s\n", $this->httpResponseHeaders)); - $save_response_headers = $this->httpResponseHeaders; - $this->DoHEADRequest( $url ); - if ( preg_match( '{^Etag:\s+"([^"]*)"\s*$}im', $this->httpResponseHeaders, $matches ) ) { - $etag = $matches[1]; - } - if ( !isset($etag) || $etag == '' ) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Still No etag in:\n%s\n", $this->httpResponseHeaders)); - } - $this->httpResponseHeaders = $save_response_headers; - } - return $etag; - } - - - /** - * DELETE a text/icalendar resource - * - * @param string $url The URL to make the request to - * @param string $etag The etag of an existing resource to be deleted, or '*' for any resource at that URL. - * - * @return int The HTTP Result Code for the DELETE - */ - function DoDELETERequest( $url, $etag = null ) { - if ( $etag != null ) { - $this->SetMatch( true, $etag ); - } - $this->DoRequest($url, "DELETE"); - return $this->httpResponseCode; - } - - - /** - * Get a single item from the server. - * - * @param string $url The URL to PROPFIND on - */ - function DoPROPFINDRequest( $url, $props, $depth = 0 ) { - $this->SetDepth($depth); - $xml = new XMLDocument( array( 'DAV:' => '', 'urn:ietf:params:xml:ns:caldav' => 'C' ) ); - $prop = new XMLElement('prop'); - foreach( $props AS $v ) { - $xml->NSElement($prop,$v); - } - - $this->DoRequest($url, "PROPFIND", $xml->Render('propfind',$prop), "text/xml"); - return $this->xmlResponse; - } - - - /** - * Get/Set the Principal URL - * - * @param $url string The Principal URL to set - */ - function PrincipalURL( $url = null ) { - if ( isset($url) ) { - $this->principal_url = $url; - } - return $this->principal_url; - } - - - /** - * Get/Set the calendar-home-set URL - * - * @param $url array of string The calendar-home-set URLs to set - */ - function CalendarHomeSet( $urls = null ) { - if ( isset($urls) ) { - if ( !is_array($urls) ) { - $urls = array($urls); - } - $this->calendar_home_set = $urls; - } - return $this->calendar_home_set; - } - - - /** - * Get/Set the calendar-home-set URL - * - * @param $urls array of string The calendar URLs to set - */ - function CalendarUrls( $urls = null ) { - if ( isset($urls) ) { - if ( !is_array($urls) ) { - $urls = array($urls); - } - $this->calendar_urls = $urls; - } - return $this->calendar_urls; - } - - - /** - * Return the first occurrence of an href inside the named tag. - * - * @param string $tagname The tag name to find the href inside of - */ - function HrefValueInside( $tagname ) { - foreach( $this->xmltags[$tagname] AS $k => $v ) { - $j = $v + 1; - if ( $this->xmlnodes[$j]['tag'] == 'DAV::href' ) { - return rawurldecode($this->xmlnodes[$j]['value']); - } - } - return null; - } - - - /** - * Return the href containing this property. Except only if it's inside a status != 200 - * - * @param string $tagname The tag name of the property to find the href for - * @param integer $which Which instance of the tag should we use - */ - function HrefForProp( $tagname, $i = 0 ) { - if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) { - $j = $this->xmltags[$tagname][$i]; - while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ) { -// printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']); - if ( $this->xmlnodes[$j]['tag'] == 'DAV::status' && $this->xmlnodes[$j]['value'] != 'HTTP/1.1 200 OK' ) { - return null; - } - } -// printf( "Node[$j]: %s\n", $this->xmlnodes[$j]['tag']); - if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) { -// printf( "Value[$j]: %s\n", $this->xmlnodes[$j]['value']); - return rawurldecode($this->xmlnodes[$j]['value']); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("xmltags[$tagname] or xmltags[$tagname][$i] is not set.")); - } - return null; - } - - - /** - * Return the href which has a resourcetype of the specified type - * - * @param string $tagname The tag name of the resourcetype to find the href for - * @param integer $which Which instance of the tag should we use - */ - function HrefForResourcetype( $tagname, $i = 0 ) { - if ( isset($this->xmltags[$tagname]) && isset($this->xmltags[$tagname][$i]) ) { - $j = $this->xmltags[$tagname][$i]; - while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::resourcetype' ); - if ( $j > 0 ) { - while( $j-- > 0 && $this->xmlnodes[$j]['tag'] != 'DAV::href' ); - if ( $j > 0 && isset($this->xmlnodes[$j]['value']) ) { - return rawurldecode($this->xmlnodes[$j]['value']); - } - } - } - return null; - } - - - /** - * Return the ... of a propstat where the status is OK - * - * @param string $nodenum The node number in the xmlnodes which is the href - */ - function GetOKProps( $nodenum ) { - $props = null; - $level = $this->xmlnodes[$nodenum]['level']; - $status = ''; - while ( $this->xmlnodes[++$nodenum]['level'] >= $level ) { - if ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::propstat' ) { - if ( $this->xmlnodes[$nodenum]['type'] == 'open' ) { - $props = array(); - $status = ''; - } else { - if ( $status == 'HTTP/1.1 200 OK' ) { - break; - } - } - } elseif ( !isset($this->xmlnodes[$nodenum]) || !is_array($this->xmlnodes[$nodenum]) ) { - break; - } elseif ( $this->xmlnodes[$nodenum]['tag'] == 'DAV::status' ) { - $status = $this->xmlnodes[$nodenum]['value']; - } else { - $props[] = $this->xmlnodes[$nodenum]; - } - } - return $props; - } - - - /** - * Attack the given URL in an attempt to find a principal URL - * - * @param string $url The URL to find the principal-URL from - */ - function FindPrincipal( $url=null ) { - $xml = $this->DoPROPFINDRequest( $url, array('resourcetype', 'current-user-principal', 'owner', 'principal-URL', 'urn:ietf:params:xml:ns:caldav:calendar-home-set'), 1); - - $principal_url = $this->HrefForProp('DAV::principal'); - - if ( !isset($principal_url) ) { - foreach( array('DAV::current-user-principal', 'DAV::principal-URL', 'DAV::owner') AS $href ) { - if ( !isset($principal_url) ) { - $principal_url = $this->HrefValueInside($href); - } - } - } - - return $this->PrincipalURL($principal_url); - } - - - /** - * Attack the given URL in an attempt to find a principal URL - * - * @param string $url The URL to find the calendar-home-set from - */ - function FindCalendarHome( $recursed=false ) { - if ( !isset($this->principal_url) ) { - $this->FindPrincipal(); - } - if ( $recursed ) { - $this->DoPROPFINDRequest( $this->principal_url, array('urn:ietf:params:xml:ns:caldav:calendar-home-set'), 0); - } - - $calendar_home = array(); - foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-home-set'] AS $k => $v ) { - if ( $this->xmlnodes[$v]['type'] != 'open' ) { - continue; - } - while( $this->xmlnodes[++$v]['type'] != 'close' && $this->xmlnodes[$v]['tag'] != 'urn:ietf:params:xml:ns:caldav:calendar-home-set' ) { -// printf( "Tag: '%s' = '%s'\n", $this->xmlnodes[$v]['tag'], $this->xmlnodes[$v]['value']); - if ( $this->xmlnodes[$v]['tag'] == 'DAV::href' && isset($this->xmlnodes[$v]['value']) ) { - $calendar_home[] = rawurldecode($this->xmlnodes[$v]['value']); - } - } - } - - if ( !$recursed && count($calendar_home) < 1 ) { - $calendar_home = $this->FindCalendarHome(true); - } - - return $this->CalendarHomeSet($calendar_home); - } - - - /** - * Find the calendars, from the calendar_home_set - */ - function FindCalendars( $recursed=false ) { - if ( !isset($this->calendar_home_set[0]) ) { - $this->FindCalendarHome(); - } - $this->DoPROPFINDRequest( $this->calendar_home_set[0], array('resourcetype','displayname','http://calendarserver.org/ns/:getctag'), 1); - - $calendars = array(); - if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar']) ) { - $calendar_urls = array(); - foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar'] AS $k => $v ) { - $calendar_urls[$this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar', $k)] = 1; - } - - foreach( $this->xmltags['DAV::href'] AS $i => $hnode ) { - $href = rawurldecode($this->xmlnodes[$hnode]['value']); - - if ( !isset($calendar_urls[$href]) ) { - continue; - } - -// printf("Seems '%s' is a calendar.\n", $href ); - - $calendar = new CalendarInfo($href); - $ok_props = $this->GetOKProps($hnode); - foreach( $ok_props AS $v ) { -// printf("Looking at: %s[%s]\n", $href, $v['tag'] ); - switch( $v['tag'] ) { - case 'http://calendarserver.org/ns/:getctag': - $calendar->getctag = $v['value']; - break; - case 'DAV::displayname': - $calendar->displayname = $v['value']; - break; - } - } - $calendar->id = rtrim(str_replace($this->calendar_home_set[0], "", $calendar->url), "/"); - $calendars[] = $calendar; - } - } - - return $this->CalendarUrls($calendars); - } - - - /** - * Find the calendars, from the calendar_home_set - */ - function GetCalendarDetails( $url = null ) { - if ( isset($url) ) { - $this->SetCalendar($url); - } - if ( !isset($this->calendar_home_set[0]) ) { - $this->FindCalendarHome(); - } - - $calendar_properties = array( 'resourcetype', 'displayname', 'http://calendarserver.org/ns/:getctag', 'urn:ietf:params:xml:ns:caldav:calendar-timezone', 'supported-report-set' ); - $this->DoPROPFINDRequest( $this->calendar_url, $calendar_properties, 0); - - $hnode = $this->xmltags['DAV::href'][0]; - $href = rawurldecode($this->xmlnodes[$hnode]['value']); - - $calendar = new CalendarInfo($href); - $ok_props = $this->GetOKProps($hnode); - foreach( $ok_props AS $k => $v ) { - $name = preg_replace( '{^.*:}', '', $v['tag'] ); - if ( isset($v['value'] ) ) { - $calendar->{$name} = $v['value']; - } /* else { - printf( "Calendar property '%s' has no text content\n", $v['tag'] ); - }*/ - } - $calendar->id = rtrim(str_replace($this->calendar_home_set[0], "", $calendar->url), "/"); - - return $calendar; - } - - - /** - * Get all etags for a calendar - */ - function GetCollectionETags( $url = null ) { - if ( isset($url) ) { - $this->SetCalendar($url); - } - - $this->DoPROPFINDRequest( $this->calendar_url, array('getetag'), 1); - - $etags = array(); - if ( isset($this->xmltags['DAV::getetag']) ) { - foreach( $this->xmltags['DAV::getetag'] AS $k => $v ) { - $href = $this->HrefForProp('DAV::getetag', $k); - if ( isset($href) && isset($this->xmlnodes[$v]['value']) ) { - $etags[$href] = $this->xmlnodes[$v]['value']; - } - } - } - - return $etags; - } - - - /** - * Get a bunch of events for a calendar with a calendar-multiget report - */ - function CalendarMultiget( $event_hrefs, $url = null ) { - if ( isset($url) ) { - $this->SetCalendar($url); - } - - $hrefs = ''; - foreach( $event_hrefs AS $k => $href ) { - $href = str_replace( rawurlencode('/'),'/',rawurlencode($href)); - $hrefs .= ''.$href.''; - } - $body = << - - -$hrefs - -EOXML; - - $this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml"); - - $events = array(); - if ( isset($this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data']) ) { - foreach( $this->xmltags['urn:ietf:params:xml:ns:caldav:calendar-data'] AS $k => $v ) { - $href = $this->HrefForProp('urn:ietf:params:xml:ns:caldav:calendar-data', $k); -// echo "Calendar-data:\n"; print_r($this->xmlnodes[$v]); - $events[$href] = $this->xmlnodes[$v]['value']; - } - } else { - foreach( $event_hrefs AS $k => $href ) { - $this->DoGETRequest($href); - $events[$href] = $this->httpResponseBody; - } - } - - return $events; - } - - - /** - * Given XML for a calendar query, return an array of the events (/todos) in the - * response. Each event in the array will have a 'href', 'etag' and '$response_type' - * part, where the 'href' is relative to the calendar and the '$response_type' contains the - * definition of the calendar data in iCalendar format. - * - * @param string $filter XML fragment which is the element of a calendar-query - * @param string $url The URL of the calendar, or empty/null to use the 'current' calendar_url - * - * @return array An array of the relative URLs, etags, and events from the server. Each element of the array will - * be an array with 'href', 'etag' and 'data' elements, corresponding to the URL, the server-supplied - * etag (which only varies when the data changes) and the calendar data in iCalendar format. - */ - function DoCalendarQuery( $filter, $url = null ) { - if ( !empty($url) ) { - $this->SetCalendar($url); - } - - $body = << - - - - - - $filter - -EOXML; - - $this->SetDepth(1); - $this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml"); - - $report = array(); - foreach( $this->xmlnodes as $k => $v ) { - switch( $v['tag'] ) { - case 'DAV::response': - if ( $v['type'] == 'open' ) { - $response = array(); - } elseif ( $v['type'] == 'close' ) { - $report[] = $response; - } - break; - case 'DAV::href': - $response['href'] = basename( rawurldecode($v['value']) ); - break; - case 'DAV::getetag': - $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']); - break; - case 'urn:ietf:params:xml:ns:caldav:calendar-data': - $response['data'] = $v['value']; - break; - } - } - return $report; - } - - - /** - * Get the events in a range from $start to $finish. The dates should be in the - * format yyyymmddThhmmssZ and should be in GMT. The events are returned as an - * array of event arrays. Each event array will have a 'href', 'etag' and 'event' - * part, where the 'href' is relative to the calendar and the event contains the - * definition of the event in iCalendar format. - * - * @param timestamp $start The start time for the period - * @param timestamp $finish The finish time for the period - * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''. - * - * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery() - */ - function GetEvents( $start = null, $finish = null, $relative_url = null ) { - $filter = ""; - if ( isset($start) && isset($finish) ) { - $range = ""; - } else { - $range = ''; - } - - $filter = << - - - $range - - - -EOFILTER; - - return $this->DoCalendarQuery($filter, $relative_url); - } - - - /** - * Get the todo's in a range from $start to $finish. The dates should be in the - * format yyyymmddThhmmssZ and should be in GMT. The events are returned as an - * array of event arrays. Each event array will have a 'href', 'etag' and 'event' - * part, where the 'href' is relative to the calendar and the event contains the - * definition of the event in iCalendar format. - * - * @param timestamp $start The start time for the period - * @param timestamp $finish The finish time for the period - * @param boolean $completed Whether to include completed tasks - * @param boolean $cancelled Whether to include cancelled tasks - * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''. - * - * @return array An array of the relative URLs, etags, and events, returned from DoCalendarQuery() @see DoCalendarQuery() - */ - function GetTodos( $start, $finish, $completed = false, $cancelled = false, $relative_url = null ) { - - if ( $start && $finish ) { - $time_range = << -EOTIME; - } else { - $time_range = ""; - } - - // Warning! May contain traces of double negatives... - $neg_cancelled = ( $cancelled === true ? "no" : "yes" ); - $neg_completed = ( $cancelled === true ? "no" : "yes" ); - - $filter = << - - - - COMPLETED - - - CANCELLED - $time_range - - - -EOFILTER; - - return $this->DoCalendarQuery($filter, $relative_url); - } - - - /** - * Get the calendar entry by UID - * - * @param uid - * @param string $relative_url The URL relative to the base_url specified when the calendar was opened. Default ''. - * @param string $component_type The component type inside the VCALENDAR. Default 'VEVENT'. - * - * @return array An array of the relative URL, etag, and calendar data returned from DoCalendarQuery() @see DoCalendarQuery() - */ - function GetEntryByUid( $uid, $relative_url = null, $component_type = 'VEVENT' ) { - $filter = ""; - if ( $uid ) { - $filter = << - - - - $uid - - - - -EOFILTER; - } - - return $this->DoCalendarQuery($filter, $relative_url); - } - - - /** - * Get the calendar entry by HREF - * - * @param string $href The href from a call to GetEvents or GetTodos etc. - * - * @return string The iCalendar of the calendar entry - */ - function GetEntryByHref( $href ) { - $href = str_replace( rawurlencode('/'),'/',rawurlencode($href)); - return $this->DoGETRequest( $href ); - } - - - /** - * Do a Sync operation. This is the fastest way to detect changes. - * - * @param string $url URL for the calendar - * @param boolean $initial It's the first synchronization - * @param boolean $support_dav_sync The CalDAV server supports sync-collection - * - * @return array of responses - */ - public function GetSync($relative_url = null, $initial = true, $support_dav_sync = false) { - if (!empty($relative_url)) { - $this->SetCalendar($relative_url); - } - - $hasToken = !$initial && isset($this->synctoken[$this->calendar_url]); - if ($support_dav_sync) { - $token = ($hasToken ? $this->synctoken[$this->calendar_url] : ""); - - $body = << - - $token - 1 - - - - - -EOXML; - } - else { - $body = << - - - - - - - - - -EOXML; - } - - $this->SetDepth(1); - $this->DoRequest($this->calendar_url, "REPORT", $body, "text/xml"); - - $report = array(); - foreach ($this->xmlnodes as $k => $v) { - switch ($v['tag']) { - case 'DAV::response': - if ($v['type'] == 'open') { - $response = array(); - } - elseif ($v['type'] == 'close') { - $report[] = $response; - } - break; - case 'DAV::href': - $response['href'] = basename( rawurldecode($v['value']) ); - break; - case 'DAV::getlastmodified': - if (isset($v['value'])) { - $response['getlastmodified'] = $v['value']; - } - else { - $response['getlastmodified'] = ''; - } - break; - case 'DAV::getetag': - $response['etag'] = preg_replace('/^"?([^"]+)"?/', '$1', $v['value']); - break; - case 'DAV::sync-token': - $this->synctoken[$this->calendar_url] = $v['value']; - break; - } - } - - // Report sync-token support on initial sync - if ($initial && $support_dav_sync && !isset($this->synctoken[$this->calendar_url])) { - ZLog::Write(LOGLEVEL_WARN, 'CalDAVClient->GetSync(): no DAV::sync-token received; did you set CALDAV_SUPPORTS_SYNC correctly?'); - } - - return $report; - } - -} \ No newline at end of file diff --git a/sources/include/z_carddav.php b/sources/include/z_carddav.php deleted file mode 100644 index a61bea5..0000000 --- a/sources/include/z_carddav.php +++ /dev/null @@ -1,900 +0,0 @@ -set_auth('username', 'password'); - * echo $carddav->get(); - * - * - * Simple vCard query - * ------------------ - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * echo $carddav->get_vcard('0126FFB4-2EB74D0A-302EA17F'); - * - * - * XML vCard query - * ------------------ - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * echo $carddav->get_xml_vcard('0126FFB4-2EB74D0A-302EA17F'); - * - * - * Check CardDAV server connection - * ------------------------------- - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * var_dump($carddav->check_connection()); - * - * - * CardDAV delete query - * -------------------- - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * $carddav->delete('0126FFB4-2EB74D0A-302EA17F'); - * - * - * CardDAV add query - * -------------------- - * $vcard = 'BEGIN:VCARD - * VERSION:3.0 - * UID:1f5ea45f-b28a-4b96-25as-ed4f10edf57b - * FN:Christian Putzke - * N:Christian;Putzke;;; - * EMAIL;TYPE=OTHER:christian.putzke@graviox.de - * END:VCARD'; - * - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * $vcard_id = $carddav->add($vcard); - * - * - * CardDAV update query - * -------------------- - * $vcard = 'BEGIN:VCARD - * VERSION:3.0 - * UID:1f5ea45f-b28a-4b96-25as-ed4f10edf57b - * FN:Christian Putzke - * N:Christian;Putzke;;; - * EMAIL;TYPE=OTHER:christian.putzke@graviox.de - * END:VCARD'; - * - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->set_auth('username', 'password'); - * $carddav->update($vcard, '0126FFB4-2EB74D0A-302EA17F'); - * - * - * CardDAV debug - * ------------- - * $carddav = new carddav_backend('https://davical.example.com/user/contacts/'); - * $carddav->enable_debug(); - * $carddav->set_auth('username', 'password'); - * $carddav->get(); - * var_dump($carddav->get_debug()); - * - * - * CardDAV server list - * ------------------- - * DAViCal: https://example.com/{resource|principal|username}/{collection}/ - * Apple Addressbook Server: https://example.com/addressbooks/users/{resource|principal|username}/{collection}/ - * memotoo: https://sync.memotoo.com/cardDAV/ - * SabreDAV: https://example.com/addressbooks/{resource|principal|username}/{collection}/ - * ownCloud: https://example.com/apps/contacts/carddav.php/addressbooks/{resource|principal|username}/{collection}/ - * SOGo: https://example.com/SOGo/dav/{resource|principal|username}/Contacts/{collection}/ - * - * - * @author Christian Putzke - * @copyright Christian Putzke - * @link http://www.graviox.de/ - * @link https://twitter.com/cputzke/ - * @since 20.07.2011 - * @version 0.6 - * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - * - */ - -class carddav_backend -{ - /** - * CardDAV PHP Version - * - * @constant string - */ - const VERSION = '0.6.c'; - - /** - * User agent displayed in http requests - * - * @constant string - */ - const USERAGENT = 'Z-Push CardDAV/'; - - /** - * CardDAV server url - * - * @var string - */ - private $url = null; - - /** - * CardDAV server url_parts - * - * @var array - */ - private $url_parts = null; - - /** - * Authentication string - * - * @var string - */ - private $auth = null; - - /** - * Authentication: username - * - * @var string - */ - private $username = null; - - /** - * Authentication: password - * - * @var string - */ - private $password = null; - - /** - * Characters used for vCard id generation - * - * @var array - */ - private $vcard_id_chars = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F'); - - /** - * CardDAV server connection (curl handle) - * - * @var resource - */ - private $curl = false; - - /** - * Debug on or off - * - * @var boolean - */ - private $debug = false; - - /** - * All available debug information - * - * @var array - */ - private $debug_information = array(); - - - /** - * Sync-token for sync-collection operations. - * - * @var array[string] - */ - private $synctoken = array(); - - - /* VCard File URL Extension - * - * @var string - */ - private $url_vcard_extension; - - /** - * Exception codes - */ - const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET = 1000; - const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_VCARD = 1001; - const EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_XML_VCARD = 1002; - const EXCEPTION_WRONG_HTTP_STATUS_CODE_DELETE = 1003; - const EXCEPTION_WRONG_HTTP_STATUS_CODE_ADD = 1004; - const EXCEPTION_WRONG_HTTP_STATUS_CODE_UPDATE = 1005; - const EXCEPTION_MALFORMED_XML_RESPONSE = 1006; - const EXCEPTION_COULD_NOT_GENERATE_NEW_VCARD_ID = 1007; - const EXCEPTION_COULD_NOT_FIND_VCARD_HREF = 1008; - - - /** - * Constructor - * Sets the CardDAV server url - * - * @param string $url CardDAV server url - * @param string $url_vcard_extension extension needed to recover the vcard, it could be empty - */ - public function __construct($url = null, $url_vcard_extension = '.vcf') { - if ($url !== null) { - $this->set_url($url); - } - $this->url_vcard_extension = $url_vcard_extension; - } - - /** - * Sets debug information - * - * @param array $debug_information Debug information - * @return void - */ - public function set_debug(array $debug_information) { - $this->debug_information[] = $debug_information; - } - - /** - * Sets the CardDAV server url - * - * @param string $url CardDAV server url - * @return void - */ - public function set_url($url) { - $this->url = $url; - - // Url always end with trailing / - if (substr($this->url, -1, 1) !== '/') { - $this->url .= '/'; - } - - $this->url_parts = parse_url($this->url); - } - - /** - * Sets authentication information - * - * @param string $username CardDAV server username - * @param string $password CardDAV server password - * @return void - */ - public function set_auth($username, $password) { - $this->username = $username; - $this->password = $password; - $this->auth = $username . ':' . $password; - } - - /** - * Gets all available debug information - * - * @return array $this->debug_information All available debug information - */ - public function get_debug() { - return $this->debug_information; - } - - /** - * Sets the CardDAV vcard url extension - * - * Most providers do requests handling Vcards with .vcf, however - * this isn't always the case and some providers (such as Google) - * returned a 404 if the .vcf extension is used - or the other - * way around, returning 404 unless .vcf is used. - * - * Both approaches are technically correct, see rfc635 - * http://tools.ietf.org/html/rfc6352 - * - * - * @param string $extension File extension - * @return void - */ - public function set_vcard_extension($extension) { - $this->url_vcard_extension = $extension; - } - - /** - * Gets all vCards including additional information from the CardDAV server. - * This operation could be slow if you have a lot of vcards. - * - * @param boolean $include_vcards Include vCards within the response (simplified only) - * @param boolean $raw Get response raw or simplified - * @params boolean $discover Only discover addressbooks - * @return string Raw or simplified XML response - */ - public function get($include_vcards = true, $raw = false, $discover = false) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->get")); - if ($discover) { - $result = $this->query($this->url, 'PROPFIND', null, null, '1'); - } - else { - $result = $this->query($this->url, 'PROPFIND'); - } - - switch ($result['http_code']) { - case 200: - case 207: - if ($raw === true) { - return $result['response']; - } - else { - return $this->simplify($result['response'], $include_vcards); - } - break; - - default: - throw new Exception('Woops, something\'s gone wrong! The CardDAV server returned the http status code ' . $result['http_code'] . '.', self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET); - break; - } - } - - - /** - * Get all vcards matching a full name or mail. - * - * @param string $pattern Pattern to search - * @param integer $limit Return only N vcards - * @param boolean $include_vcards Include vCards within the response (simplified only) - * @param boolean $raw Get response raw or simplified - * @param boolean $support_fn_search If the server supports searchs by fn - * @return string Raw or simplified XML response - */ - public function search_vcards($pattern, $limit, $include_vcards = true, $raw = false, $support_fn_search = false) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->search_vcards")); - if ($support_fn_search) { - $xml = << - - - - - - - - - - $pattern - - - - $limit - - -EOFCONTENTSEARCH; - } - else { - $xml = << - - - - - - - - - - $pattern - - - $pattern - - - $pattern - - - - $limit - - -EOFCONTENTSEARCH; - } - - return $this->do_query_report($xml, $include_vcards, $raw, true); - } - - /** - * Get all vcards or changes since the last sync. - * - * @param boolean $initial If the sync should be full - * @param boolean $include_vcards If the vCards should be included within the response - * @param boolean $support_carddav_sync If the cardDAV server supports sync-collection operations (DAViCal supports it) - * @return string Simplified XML response - */ - public function do_sync($initial = true, $include_vcards = false, $support_carddav_sync = false) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->do_sync")); - - if ($support_carddav_sync) { - if ($initial) { - $token = ""; - } - else { - $token = $this->synctoken[$this->url]; - } - - $xml = << - - $token - 1 - - - - - -EOFXMLINITIALSYNC; - - return $this->do_query_report($xml, $include_vcards, false); - } - else { - return $this->get($include_vcards, false); - } - } - - - /** - * Do a REPORT query against the server - * - * @param string $xml XML body request - * @param boolean $include_vcards If the vCards should be included within the response - * @param boolean $raw If the response should be raw or XML simplified - * @param boolean $remove_duplicates If we will apply uniqness to the response vcards - * @return string - */ - private function do_query_report($xml, $include_vcards = true, $raw = false, $remove_duplicates = false) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->do_query_report")); - $result = $this->query($this->url, 'REPORT', $xml, 'text/xml'); - - try { - switch ($result['http_code']) { - case 200: - case 207: - if ($raw === true) { - return $result['response']; - } - else { - return $this->simplify($result['response'], $include_vcards, $remove_duplicates); - } - break; - - default: - throw new Exception('Woops, something\'s gone wrong! The CardDAV server returned the http status code ' . $result['http_code'] . '.', self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET); - break; - } - } - catch(Exception $ex) { - // vcard not found - if ($ex->getCode() == self::EXCEPTION_COULD_NOT_FIND_VCARD_HREF) { - if (strlen($this->url_vcard_extension) == 0 || stripos($xml, $this->url_vcard_extension) === FALSE) { - throw $ex; - } - else { - // try to do the same without the $this->url_vcard_extension - return $this->do_query_report(str_ireplace($this->url_vcard_extension, "", $xml), $include_vcards, $raw, $remove_duplicates); - } - } - else { - throw $ex; - } - } - } - - /** - * Gets a clean vCard from the CardDAV server - * - * @param string $vcard_href vCard href on the CardDAV server - * @return string vCard (text/vcard) - */ - private function get_vcard($vcard_href) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->get_vcard")); - $url = $this->url_parts['scheme'] . '://' . $this->url_parts['host'] . ':' . $this->url_parts['port'] . $vcard_href; - $result = $this->query($url, 'GET'); - - switch ($result['http_code']) { - case 200: - case 207: - return $result['response']; - break; - - default: - throw new Exception('Woops, something\'s gone wrong! The CardDAV server returned the http status code ' . $result['http_code'] . '.', self::EXCEPTION_WRONG_HTTP_STATUS_CODE_GET_VCARD); - break; - } - } - - /** - * Gets a vCard + XML from the CardDAV Server - * - * @param string $vcard_id vCard id on the CardDAV Server - * @return string Raw or simplified vCard (text/xml) - */ - public function get_xml_vcard($vcard_id) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->get_xml_vcard")); - $href = $this->url_parts['path'] . str_replace($this->url_vcard_extension, null, $vcard_id) . $this->url_vcard_extension; - - // If we don't ask for allprop, SOGo doesn't return the content_type - $xml = << - - - - - - - - - $href - -EOFXMLGETXMLVCARD; - - return $this->do_query_report($xml); - } - - /** - * Enables the debug mode - * - * @return void - */ - public function enable_debug() { - $this->debug = true; - } - - /** - * Checks if the CardDAV server is reachable - * - * @return boolean - */ - public function check_connection() { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->check_connection")); - $result = $this->query($this->url, 'OPTIONS'); - - $status = false; - switch($result['http_code']) { - case 200: - case 207: - $status = true; - break; - } - - return $status; - } - - /** - * Deletes an entry from the CardDAV server - * - * @param string $vcard_id vCard id on the CardDAV server - * @return boolean - */ - public function delete($vcard_id) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->delete")); - $result = $this->query($this->url . $vcard_id . $this->url_vcard_extension, 'DELETE'); - - - switch ($result['http_code']) { - case 204: - return true; - break; - - default: - throw new Exception('Woops, something\'s gone wrong! The CardDAV server returned the http status code ' . $result['http_code'] . '.', self::EXCEPTION_WRONG_HTTP_STATUS_CODE_DELETE); - break; - } - } - - /** - * Adds an entry to the CardDAV server - * - * @param string $vcard vCard - * @param string $vcard_id vCard id on the CardDAV server - * @return string The new vCard id - */ - public function add($vcard, $vcard_id = null) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->add")); - if ($vcard_id === null) { - $vcard_id = $this->generate_vcard_id(); - } - - $vcard = str_replace("\nEND:VCARD","\nUID:" . $vcard_id . "\r\nEND:VCARD", $vcard); - $result = $this->query($this->url . $vcard_id . $this->url_vcard_extension, 'PUT', $vcard, 'text/vcard'); - - - switch($result['http_code']) { - case 201: - case 204: - return $vcard_id; - break; - - default: - throw new Exception('Woops, something\'s gone wrong! The CardDAV server returned the http status code ' . $result['http_code'] . '.', self::EXCEPTION_WRONG_HTTP_STATUS_CODE_ADD); - break; - } - } - - /** - * Updates an entry to the CardDAV server - * - * @param string $vcard vCard - * @param string $vcard_id vCard id on the CardDAV server - * @return boolean - */ - public function update($vcard, $vcard_id) { - try { - return $this->add($vcard, $vcard_id); - } - catch (Exception $e) { - throw new Exception($e->getMessage(), self::EXCEPTION_WRONG_HTTP_STATUS_CODE_UPDATE); - } - } - - /** - * Simplify CardDAV XML response - * - * @param string $response CardDAV XML response - * @param boolean $include_vcards Include vCards or not - * @param boolean $remove_duplicates If we will apply uniqness to the response vcards - * @return string Simplified CardDAV XML response - */ - private function simplify($response, $include_vcards = true, $remove_duplicates = false) { - $response = $this->remove_namespaces($response); - - try { - $xml = new SimpleXMLElement($response); - } - catch(Exception $e) { - throw new Exception('The XML response seems to be malformed and can\'t be simplified!', self::EXCEPTION_MALFORMED_XML_RESPONSE, $e); - } - - if (!empty($xml->{'sync-token'})) { - $this->synctoken[$this->url] = $xml->{'sync-token'}; - } - - $simplified_xml = new XMLWriter(); - $simplified_xml->openMemory(); - $simplified_xml->setIndent(4); - - $simplified_xml->startDocument('1.0', 'utf-8'); - $simplified_xml->startElement('response'); - - if (!empty($xml->response)) { - $unique_etags = array(); - - foreach ($xml->response as $response) { - if (isset($response->propstat)) { - if ((strlen($this->url_vcard_extension) > 0 && preg_match('/'.$this->url_vcard_extension.'/', $response->href) && - !(isset($response->propstat->prop->resourcetype) && isset($response->propstat->prop->resourcetype->addressbook))) - || preg_match('/vcard/', $response->propstat->prop->getcontenttype) || isset($response->propstat->prop->{'address-data'}) || isset($response->propstat->prop->{'addressbook-data'})) { - // It's a vcard - $id = basename($response->href); - $id = str_replace($this->url_vcard_extension, null, $id); - - if (!empty($id)) { - $simplified_xml->startElement('element'); - $simplified_xml->writeElement('id', $id); - $simplified_xml->writeElement('etag', str_replace('"', null, $response->propstat->prop->getetag)); - $simplified_xml->writeElement('last_modified', $response->propstat->prop->getlastmodified); - - if ($include_vcards === true) { - if (isset($response->propstat->prop->{'address-data'})) { - // We already have the full vcard - $simplified_xml->writeElement('vcard', $response->propstat->prop->{'address-data'}); - } - else if (isset($response->propstat->prop->{'addressbook-data'})) { - // We already have the full vcard, also - $simplified_xml->writeElement('vcard', $response->propstat->prop->{'addressbook-data'}); - } - else { - // We don't have the vcard, we need to get it. We never should hit here, it would mean a buggy server - $simplified_xml->writeElement('vcard', $this->get_vcard($response->href)); - } - } - $simplified_xml->endElement(); - } - } - else if (isset($response->propstat->prop->resourcetype->addressbook)) { - // It's an addressbook - if (isset($response->propstat->prop->href)) { - $href = $response->propstat->prop->href; - } - else if (isset($response->href)) { - $href = $response->href; - } - else { - $href = null; - } - - $url = str_replace($this->url_parts['path'], null, $this->url) . $href; - $simplified_xml->startElement('addressbook_element'); - $simplified_xml->writeElement('display_name', $response->propstat->prop->displayname); - $simplified_xml->writeElement('url', $url); - $simplified_xml->writeElement('last_modified', $response->propstat->prop->getlastmodified); - $simplified_xml->endElement(); - } - } - else { - // We don't have a propstat node, so it will be an error answer - if (isset($response->status) && preg_match('/404 Not Found/', $response->status)) { - throw new Exception('Not found!', self::EXCEPTION_COULD_NOT_FIND_VCARD_HREF); - } - else { - throw new Exception('The XML response is an error message and can\'t be simplified!', self::EXCEPTION_MALFORMED_XML_RESPONSE); - } - } - } - - unset($unique_etags); - } - - $simplified_xml->endElement(); - $simplified_xml->endDocument(); - - return $simplified_xml->outputMemory(); - } - - /** - * Cleans CardDAV XML response - * - * @param string $response CardDAV XML response - * @return string $response Cleaned CardDAV XML response - */ - private function remove_namespaces($response) { -// $response = preg_replace('/<[a-z0-9]+:(.*)/i', '<$1', $response); -// $response = preg_replace('/<\/[a-z0-9]+:(.*)/i', ' - - - - - - -EOFXSL; - - $dom = new DOMDocument(); - $dom->loadXML($response); - $stylesheet = new DOMDocument(); - $stylesheet->loadXML($xsl); - $xsltprocessor = new XSLTProcessor(); - $xsltprocessor->importStylesheet($stylesheet); - - $response = $xsltprocessor->transformToXML($dom); - $dom = null; - $stylesheet = null; - $xsltprocessor = null; - - return $response; - } - - /** - * Curl initialization - * - * @return void - */ - public function curl_init() { - if ($this->curl === false) { - $this->curl = curl_init(); - curl_setopt($this->curl, CURLOPT_HEADER, true); - curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false); - curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($this->curl, CURLOPT_USERAGENT, self::USERAGENT.self::VERSION); - - if ($this->auth !== null) { - curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - curl_setopt($this->curl, CURLOPT_USERPWD, $this->auth); - } - } - } - - /** - * Query the CardDAV server via curl and returns the response - * - * @param string $url CardDAV server URL - * @param string $method HTTP method like (OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE) - * @param string $content Content for CardDAV queries - * @param string $content_type Set content type - * @param string $depth Set Depth - * @return array Raw CardDAV Response and http status code - */ - private function query($url, $method, $content = null, $content_type = null, $depth = "infinity") { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendCardDAV->carddav_backend->query - '%s' '%s' '%s' '%s'", $url, $method, $content, $content_type)); - - $this->curl_init(); - - curl_setopt($this->curl, CURLOPT_URL, $url); - curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method); - - if ($content !== null) { - curl_setopt($this->curl, CURLOPT_POST, true); - curl_setopt($this->curl, CURLOPT_POSTFIELDS, $content); - } - else { - curl_setopt($this->curl, CURLOPT_POST, false); - curl_setopt($this->curl, CURLOPT_POSTFIELDS, null); - } - - if ($content_type !== null) { - curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-type: '.$content_type. '; charset=utf-8', 'Depth: '.$depth)); - } - else { - curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Depth: '.$depth)); - } - - $complete_response = curl_exec($this->curl); - $header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE); - $http_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); - $header = trim(substr($complete_response, 0, $header_size)); - $response = substr($complete_response, $header_size); - - $return = array( - 'response' => $response, - 'http_code' => $http_code - ); - - if ($this->debug === true) { - $debug = $return; - $debug['url'] = $url; - $debug['method'] = $method; - $debug['content'] = $content; - $debug['content_type'] = $content_type; - $debug['header'] = $header; - $this->set_debug($debug); - } - - return $return; - } - - /** - * Returns a valid and unused vCard id - * - * @return string $vcard_id Valid vCard id - */ - private function generate_vcard_id() { - $vcard_id = null; - - for ($number = 0; $number <= 25; $number ++) { - if ($number == 8 || $number == 17) { - $vcard_id .= '-'; - } - else { - $vcard_id .= $this->vcard_id_chars[mt_rand(0, (count($this->vcard_id_chars) - 1))]; - } - } - - try { - $carddav = new carddav_backend($this->url); - $carddav->set_auth($this->username, $this->password); - - $result = $carddav->query($this->url . $vcard_id . $this->url_vcard_extension, 'GET'); - - if ($result['http_code'] !== 404) { - $vcard_id = $this->generate_vcard_id(); - } - - return $vcard_id; - } - catch (Exception $e) { - throw new Exception($e->getMessage(), self::EXCEPTION_COULD_NOT_GENERATE_NEW_VCARD_ID); - } - } - - /** - * Destructor - * Close curl connection if it's open - * - * @return void - */ - public function __destruct() { - $this->disconnect(); - } - - /** - * Disconnect curl connection - * - */ - public function disconnect() { - if ($this->curl !== false) { - curl_close($this->curl); - $this->curl = false; - } - } -} \ No newline at end of file diff --git a/sources/include/z_syslog.php b/sources/include/z_syslog.php deleted file mode 100644 index 0a16d7b..0000000 --- a/sources/include/z_syslog.php +++ /dev/null @@ -1,108 +0,0 @@ - 0) { - $syslog_message = "<{$pri}>" . date('M d H:i:s ') . self::$program . ': ' . $line; - socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, self::$hostname, self::$port); - } - } - socket_close($sock); - } - - return true; - } - - /** - * Converts the ZLog level to SYSLOG level. - * - * @params int $loglevel Z-Push LogLevel - * - * @access private - * @return SYSLOG_LEVEL or false - */ - private static function zlogLevel2SyslogLevel($loglevel) { - switch($loglevel) { - case LOGLEVEL_OFF: return false; break; - case LOGLEVEL_FATAL: return LOG_ALERT; break; - case LOGLEVEL_ERROR: return LOG_ERR; break; - case LOGLEVEL_WARN: return LOG_WARNING; break; - case LOGLEVEL_INFO: return LOG_INFO; break; - case LOGLEVEL_DEBUG: return LOG_DEBUG; break; - case LOGLEVEL_WBXML: return LOG_DEBUG; break; - case LOGLEVEL_DEVICEID: return LOG_DEBUG; break; - case LOGLEVEL_WBXMLSTACK: return LOG_DEBUG; break; - } - } -} diff --git a/sources/index.php b/sources/index.php deleted file mode 100644 index dceced5..0000000 --- a/sources/index.php +++ /dev/null @@ -1,257 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -// #190, KD 2015-06-08 - We are missing the flags to truncate the buffer in PHP >= 5.4 -if (version_compare(phpversion(), '5.4.0') < 0) { - ob_start(null, 1048576); -} -else { - ob_start(null, 1048576, PHP_OUTPUT_HANDLER_STDFLAGS); -} - -// ignore user abortions because this can lead to weird errors - see ZP-239 -ignore_user_abort(true); - -require_once 'vendor/autoload.php'; -require_once 'config.php'; - -if (defined('LOG_MEMORY_PROFILER') && LOG_MEMORY_PROFILER) { - if (function_exists('memprof_enable')) { - memprof_enable(); - } - else { - ZLog::Write(LOGLEVEL_WARN, "Memory profiler is enabled but the php-pecl-memprof extension was not found. Install and enable it"); - } -} - - // Attempt to set maximum execution time - ini_set('max_execution_time', SCRIPT_TIMEOUT); - set_time_limit(SCRIPT_TIMEOUT); - - try { - // check config & initialize the basics - ZPush::CheckConfig(); - Request::Initialize(); - ZLog::Initialize(); - - $autenticationInfo = Request::AuthenticationInfo(); - $GETUser = Request::GetGETUser(); - - ZLog::Write(LOGLEVEL_DEBUG,"-------- Start"); - ZLog::Write(LOGLEVEL_INFO, - sprintf("Version='%s' method='%s' from='%s' cmd='%s' getUser='%s' devId='%s' devType='%s'", - @constant('ZPUSH_VERSION'), Request::GetMethod(), Request::GetRemoteAddr(), - Request::GetCommand(), $GETUser, Request::GetDeviceID(), Request::GetDeviceType())); - - // Stop here if this is an OPTIONS request - if (Request::IsMethodOPTIONS()) { - if (!$autenticationInfo || !$GETUser) { - throw new AuthenticationRequiredException("Access denied. Please send authentication information"); - } - else { - throw new NoPostRequestException("Options request", NoPostRequestException::OPTIONS_REQUEST); - } - } - - ZPush::CheckAdvancedConfig(); - - // Process request headers and look for AS headers - Request::ProcessHeaders(); - - // Check required GET parameters - if(Request::IsMethodPOST() && (Request::GetCommandCode() === false || !Request::GetDeviceID() || !Request::GetDeviceType())) - throw new FatalException("Requested the Z-Push URL without the required GET parameters"); - - - // This won't be useful with Zarafa, but it will be with standalone Z-Push - if (defined('PRE_AUTHORIZE_USERS') && PRE_AUTHORIZE_USERS === true) { - if (!Request::IsMethodGET()) { - // Check if User/Device are authorized - if (ZPush::GetDeviceManager()->GetUserDevicePermission($GETUser, Request::GetDeviceID()) != SYNC_COMMONSTATUS_SUCCESS) { - throw new AuthenticationRequiredException("Access denied. Username and Device not authorized"); - } - } - } - - // Load the backend - $backend = ZPush::GetBackend(); - - // always request the authorization header - if (!$autenticationInfo || !$GETUser) - throw new AuthenticationRequiredException("Access denied. Please send authorisation information"); - - // check the provisioning information - if (PROVISIONING === true && Request::IsMethodPOST() && ZPush::CommandNeedsProvisioning(Request::GetCommandCode()) && - ((Request::WasPolicyKeySent() && Request::GetPolicyKey() == 0) || ZPush::GetDeviceManager()->ProvisioningRequired(Request::GetPolicyKey())) && - (LOOSE_PROVISIONING === false || - (LOOSE_PROVISIONING === true && Request::WasPolicyKeySent()))) - //TODO for AS 14 send a wbxml response - throw new ProvisioningRequiredException(); - - // most commands require an authenticated user - if (ZPush::CommandNeedsAuthentication(Request::GetCommandCode())) - RequestProcessor::Authenticate(); - - // Do the actual processing of the request - if (Request::IsMethodGET()) - throw new NoPostRequestException("This is the Z-Push location and can only be accessed by Microsoft ActiveSync-capable devices", NoPostRequestException::GET_REQUEST); - - // Do the actual request - header(ZPush::GetServerHeader()); - - // announce the supported AS versions (if not already sent to device) - if (ZPush::GetDeviceManager()->AnnounceASVersion()) { - $versions = ZPush::GetSupportedProtocolVersions(true); - ZLog::Write(LOGLEVEL_INFO, sprintf("Announcing latest AS version to device: %s", $versions)); - header("X-MS-RP: ". $versions); - } - - RequestProcessor::Initialize(); - RequestProcessor::HandleRequest(); - - // eventually the RequestProcessor wants to send other headers to the mobile - foreach (RequestProcessor::GetSpecialHeaders() as $header) - header($header); - - // log amount of data transferred - // TODO check $len when streaming more data (e.g. Attachments), as the data will be send chunked - ZPush::GetDeviceManager()->SentData(ob_get_length()); - } - - catch (NoPostRequestException $nopostex) { - $len = ob_get_length(); - if ($len) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Cleaning %d octets of data", $len)); - ob_clean(); - } - - if ($nopostex->getCode() == NoPostRequestException::OPTIONS_REQUEST) { - header(ZPush::GetServerHeader()); - header(ZPush::GetSupportedProtocolVersions()); - header(ZPush::GetSupportedCommands()); - ZLog::Write(LOGLEVEL_INFO, $nopostex->getMessage()); - } - else if ($nopostex->getCode() == NoPostRequestException::GET_REQUEST) { - if (Request::GetUserAgent()) - ZLog::Write(LOGLEVEL_INFO, sprintf("User-agent: '%s'", Request::GetUserAgent())); - if (!headers_sent() && $nopostex->showLegalNotice()) - ZPush::PrintZPushLegal('GET not supported', $nopostex->getMessage()); - } - } - - catch (Exception $ex) { - $len = ob_get_length(); - if ($len) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Cleaning %d octets of data", $len)); - ob_clean(); - } - - if (Request::GetUserAgent()) - ZLog::Write(LOGLEVEL_INFO, sprintf("User-agent: '%s'", Request::GetUserAgent())); - $exclass = get_class($ex); - - if(!headers_sent()) { - if ($ex instanceof ZPushException) { - header('HTTP/1.1 '. $ex->getHTTPCodeString()); - foreach ($ex->getHTTPHeaders() as $h) - header($h); - } - // something really unexpected happened! - else - header('HTTP/1.1 500 Internal Server Error'); - } - else - ZLog::Write(LOGLEVEL_FATAL, "Exception: ($exclass) - headers were already sent. Message: ". $ex->getMessage()); - - if ($ex instanceof AuthenticationRequiredException) { - ZPush::PrintZPushLegal($exclass, sprintf('
%s
',$ex->getMessage())); - - // log the failed login attemt e.g. for fail2ban - if (defined('LOGAUTHFAIL') && LOGAUTHFAIL != false) - ZLog::Write(LOGLEVEL_WARN, sprintf("IP: %s failed to authenticate user '%s'", Request::GetRemoteAddr(), Request::GetAuthUser()? Request::GetAuthUser(): Request::GetGETUser() )); - } - - // This could be a WBXML problem.. try to get the complete request - else if ($ex instanceof WBXMLException) { - ZLog::Write(LOGLEVEL_FATAL, "Request could not be processed correctly due to a WBXMLException. Please report this including WBXML debug data logged. Be aware that the debug data could contain confidential information."); - } - - // Try to output some kind of error information. This is only possible if - // the output had not started yet. If it has started already, we can't show the user the error, and - // the device will give its own (useless) error message. - else if (!($ex instanceof ZPushException) || $ex->showLegalNotice()) { - $cmdinfo = (Request::GetCommand())? sprintf(" processing command %s", Request::GetCommand()): ""; - $extrace = $ex->getTrace(); - $trace = (!empty($extrace))? "\n\nTrace:\n". print_r($extrace,1):""; - ZPush::PrintZPushLegal($exclass . $cmdinfo, sprintf('
%s
',$ex->getMessage() . $trace)); - } - - // Announce exception to process loop detection - if (ZPush::GetDeviceManager(false)) - ZPush::GetDeviceManager()->AnnounceProcessException($ex); - - // Announce exception if the TopCollector if available - ZPush::GetTopCollector()->AnnounceInformation(get_class($ex), true); - } - - // FinishResponse - ZPush::FinishResponse(); - // destruct backend after all data is on the stream - ZPush::GetBackend()->Logoff(); - - // save device data if the DeviceManager is available - if (ZPush::GetDeviceManager(false)) - ZPush::GetDeviceManager()->Save(); - - // end gracefully - ZLog::WriteEnd(); - -if (defined('LOG_MEMORY_PROFILER') && LOG_MEMORY_PROFILER) { - if (function_exists('memprof_enable')) { - // Be aware that the pid is not unique, so we will overwrite the output in some cases. But using the pid will be easier to relate the dump with the log lines - memprof_dump_callgrind(fopen(LOG_MEMORY_PROFILER_FILE . "_" . getmypid(), "w")); - } -} \ No newline at end of file diff --git a/sources/lib/core/asdevice.php b/sources/lib/core/asdevice.php deleted file mode 100644 index ea0b025..0000000 --- a/sources/lib/core/asdevice.php +++ /dev/null @@ -1,689 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ASDevice extends StateObject { - const UNDEFINED = -1; - // content data - const FOLDERUUID = 1; - const FOLDERTYPE = 2; - const FOLDERSUPPORTEDFIELDS = 3; - const FOLDERSYNCSTATUS = 4; - - // expected values for not set member variables - protected $unsetdata = array( - 'useragenthistory' => array(), - 'hierarchyuuid' => false, - 'contentdata' => array(), - 'wipestatus' => SYNC_PROVISION_RWSTATUS_NA, - 'wiperequestedby' => false, - 'wiperequestedon' => false, - 'wipeactionon' => false, - 'lastupdatetime' => 0, - 'conversationmode' => false, - 'policies' => array(), - 'policykey' => self::UNDEFINED, - 'forcesave' => false, - 'asversion' => false, - 'ignoredmessages' => array(), - 'announcedASversion' => false, - 'foldersynccomplete' => true, - ); - - static private $loadedData; - protected $newdevice; - protected $hierarchyCache; - protected $ignoredMessageIds; - - /** - * AS Device constructor - * - * @param string $devid - * @param string $devicetype - * @param string $getuser - * @param string $useragent - * - * @access public - * @return - */ - public function ASDevice($devid, $devicetype, $getuser, $useragent) { - $this->deviceid = $devid; - $this->devicetype = $devicetype; - list ($this->deviceuser, $this->domain) = Utils::SplitDomainUser($getuser); - $this->useragent = $useragent; - $this->firstsynctime = time(); - $this->newdevice = true; - $this->ignoredMessageIds = array(); - } - - /** - * initializes the ASDevice with previousily saved data - * - * @param mixed $stateObject the StateObject containing the device data - * @param boolean $semanticUpdate indicates if data relevant for all users should be cross checked (e.g. wipe requests) - * - * @access public - * @return - */ - public function SetData($stateObject, $semanticUpdate = true) { - if (!($stateObject instanceof StateObject) || !isset($stateObject->devices) || !is_array($stateObject->devices)) return; - - // is information about this device & user available? - if (isset($stateObject->devices[$this->deviceuser]) && $stateObject->devices[$this->deviceuser] instanceof ASDevice) { - // overwrite local data with data from the saved object - $this->SetDataArray($stateObject->devices[$this->deviceuser]->GetDataArray()); - $this->newdevice = false; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ASDevice data loaded for user: '%s'", $this->deviceuser)); - } - - // check if RWStatus from another user on same device may require action - if ($semanticUpdate && count($stateObject->devices) > 1) { - foreach ($stateObject->devices as $user=>$asuserdata) { - if ($user == $this->user) continue; - - // another user has a required action on this device - if (isset($asuserdata->wipeStatus) && $asuserdata->wipeStatus > SYNC_PROVISION_RWSTATUS_OK) { - ZLog::Write(LOGLEVEL_INFO, sprintf("User '%s' has requested a remote wipe for this device on '%s'", $asuserdata->wipeRequestBy, strftime("%Y-%m-%d %H:%M", $asuserdata->wipeRequstOn))); - - // reset status to PENDING if wipe was executed before - $this->wipeStatus = ($asuserdata->wipeStatus & SYNC_PROVISION_RWSTATUS_WIPED)?SYNC_PROVISION_RWSTATUS_PENDING:$asuserdata->wipeStatus; - $this->wipeRequestBy = $asuserdata->wipeRequestBy; - $this->wipeRequestOn = $asuserdata->wipeRequestOn; - $this->wipeActionOn = $asuserdata->wipeActionOn; - break; - } - } - } - - self::$loadedData = $stateObject; - $this->changed = false; - } - - /** - * Returns the current AS Device in it's StateObject - * If the data was not changed, it returns false (no need to update any data) - * - * @access public - * @return array/boolean - */ - public function GetData() { - if (! $this->changed) - return false; - - // device was updated - $this->lastupdatetime = time(); - unset($this->ignoredMessageIds); - - if (!isset(self::$loadedData) || !isset(self::$loadedData->devices) || !is_array(self::$loadedData->devices)) { - self::$loadedData = new StateObject(); - $devices = array(); - } - else - $devices = self::$loadedData->devices; - - $devices[$this->deviceuser] = $this; - - // check if RWStatus has to be updated so it can be updated for other users on same device - if (isset($this->wipeStatus) && $this->wipeStatus > SYNC_PROVISION_RWSTATUS_OK) { - foreach ($devices as $user=>$asuserdata) { - if ($user == $this->deviceuser) continue; - if (isset($this->wipeStatus)) $asuserdata->wipeStatus = $this->wipeStatus; - if (isset($this->wipeRequestBy)) $asuserdata->wipeRequestBy = $this->wipeRequestBy; - if (isset($this->wipeRequestOn)) $asuserdata->wipeRequestOn = $this->wipeRequestOn; - if (isset($this->wipeActionOn)) $asuserdata->wipeActionOn = $this->wipeActionOn; - $devices[$user] = $asuserdata; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Updated remote wipe status for user '%s' on the same device", $user)); - } - } - self::$loadedData->devices = $devices; - return self::$loadedData; - } - - /** - * Removes internal data from the object, so this data can not be exposed - * - * @access public - * @return boolean - */ - public function StripData() { - unset($this->changed); - unset($this->unsetdata); - unset($this->hierarchyCache); - unset($this->forceSave); - unset($this->newdevice); - unset($this->ignoredMessageIds); - - if (isset($this->ignoredmessages) && is_array($this->ignoredmessages)) { - $imessages = $this->ignoredmessages; - $unserializedMessage = array(); - foreach ($imessages as $im) { - $im->asobject = unserialize($im->asobject); - $im->asobject->StripData(); - $unserializedMessage[] = $im; - } - $this->ignoredmessages = $unserializedMessage; - } - - return true; - } - - /** - * Indicates if the object was just created - * - * @access public - * @return boolean - */ - public function IsNewDevice() { - return (isset($this->newdevice) && $this->newdevice === true); - } - - - /**---------------------------------------------------------------------------------------------------------- - * Non-standard Getter and Setter - */ - - /** - * Returns the user agent of this device - * - * @access public - * @return string - */ - public function GetDeviceUserAgent() { - if (!isset($this->useragent) || !$this->useragent) - return "unknown"; - - return $this->useragent; - } - - /** - * Returns the user agent history of this device - * - * @access public - * @return string - */ - public function GetDeviceUserAgentHistory() { - return $this->useragentHistory; - } - - /** - * Sets the useragent of the current request - * If this value is alreay available, no update is done - * - * @param string $useragent - * - * @access public - * @return boolean - */ - public function SetUserAgent($useragent) { - if ($useragent == $this->useragent || $useragent === false || $useragent === Request::UNKNOWN) - return true; - - // save the old user agent, if available - if ($this->useragent != "") { - // [] = changedate, previous user agent - $a = $this->useragentHistory; - - // only add if this agent was not seen before - if (! in_array(array(true, $this->useragent), $a)) { - $a[] = array(time(), $this->useragent); - $this->useragentHistory = $a; - } - } - $this->useragent = $useragent; - return true; - } - - /** - * Sets the current remote wipe status - * - * @param int $status - * @param string $requestedBy - * @access public - * @return int - */ - public function SetWipeStatus($status, $requestedBy = false) { - // force saving the updated information if there was a transition between the wiping status - if ($this->wipeStatus > SYNC_PROVISION_RWSTATUS_OK && $status > SYNC_PROVISION_RWSTATUS_OK) - $this->forceSave = true; - - if ($requestedBy != false) { - $this->wipeRequestedBy = $requestedBy; - $this->wipeRequestedOn = time(); - } - else { - $this->wipeActionOn = time(); - } - - $this->wipeStatus = $status; - - if ($this->wipeStatus > SYNC_PROVISION_RWSTATUS_PENDING) - ZLog::Write(LOGLEVEL_INFO, sprintf("ASDevice id '%s' was %s remote wiped on %s. Action requested by user '%s' on %s", - $this->deviceid, ($this->wipeStatus == SYNC_PROVISION_RWSTATUS_REQUESTED ? "requested to be": "sucessfully"), - strftime("%Y-%m-%d %H:%M", $this->wipeActionOn), $this->wipeRequestedBy, strftime("%Y-%m-%d %H:%M", $this->wipeRequestedOn))); - } - - /** - * Sets the deployed policy key - * - * @param int $policykey - * - * @access public - * @return - */ - public function SetPolicyKey($policykey) { - $this->policykey = $policykey; - if ($this->GetWipeStatus() == SYNC_PROVISION_RWSTATUS_NA) - $this->wipeStatus = SYNC_PROVISION_RWSTATUS_OK; - } - - /** - * Adds a messages which was ignored to the device data - * - * @param StateObject $ignoredMessage - * - * @access public - * @return boolean - */ - public function AddIgnoredMessage($ignoredMessage) { - // we should have all previousily ignored messages in an id array - if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) { - foreach($this->ignoredMessages as $oldMessage) { - if (!isset($this->ignoredMessageIds[$oldMessage->folderid])) - $this->ignoredMessageIds[$oldMessage->folderid] = array(); - $this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id; - } - } - - // serialize the AS object - if available - if (isset($ignoredMessage->asobject)) - $ignoredMessage->asobject = serialize($ignoredMessage->asobject); - - // try not to add the same message several times - if (isset($ignoredMessage->folderid) && isset($ignoredMessage->id)) { - if (!isset($this->ignoredMessageIds[$ignoredMessage->folderid])) - $this->ignoredMessageIds[$ignoredMessage->folderid] = array(); - - if (in_array($ignoredMessage->id, $this->ignoredMessageIds[$ignoredMessage->folderid])) - $this->RemoveIgnoredMessage($ignoredMessage->folderid, $ignoredMessage->id); - - $this->ignoredMessageIds[$ignoredMessage->folderid][] = $ignoredMessage->id; - $msges = $this->ignoredMessages; - $msges[] = $ignoredMessage; - $this->ignoredMessages = $msges; - - return true; - } - else { - $msges = $this->ignoredMessages; - $msges[] = $ignoredMessage; - $this->ignoredMessages = $msges; - ZLog::Write(LOGLEVEL_WARN, "ASDevice->AddIgnoredMessage(): added message has no folder/id"); - return true; - } - } - - /** - * Removes message in the list of ignored messages - * - * @param string $folderid parent folder id of the message - * @param string $id message id - * - * @access public - * @return boolean - */ - public function RemoveIgnoredMessage($folderid, $id) { - // we should have all previousily ignored messages in an id array - if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) { - foreach($this->ignoredMessages as $oldMessage) { - if (!isset($this->ignoredMessageIds[$oldMessage->folderid])) - $this->ignoredMessageIds[$oldMessage->folderid] = array(); - $this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id; - } - } - - $foundMessage = false; - // there are ignored messages in that folder - if (isset($this->ignoredMessageIds[$folderid])) { - // resync of a folder.. we should remove all previousily ignored messages - if ($id === false || in_array($id, $this->ignoredMessageIds[$folderid], true)) { - $ignored = $this->ignoredMessages; - $newMessages = array(); - foreach ($ignored as $im) { - if ($im->folderid == $folderid) { - if ($id === false || $im->id === $id) { - $foundMessage = true; - if (count($this->ignoredMessageIds[$folderid]) == 1) { - unset($this->ignoredMessageIds[$folderid]); - } - else { - unset($this->ignoredMessageIds[$folderid][array_search($id, $this->ignoredMessageIds[$folderid])]); - } - continue; - } - else - $newMessages[] = $im; - } - } - $this->ignoredMessages = $newMessages; - } - } - - return $foundMessage; - } - - /** - * Indicates if a message is in the list of ignored messages - * - * @param string $folderid parent folder id of the message - * @param string $id message id - * - * @access public - * @return boolean - */ - public function HasIgnoredMessage($folderid, $id) { - // we should have all previousily ignored messages in an id array - if (count($this->ignoredMessages) != count($this->ignoredMessageIds)) { - foreach($this->ignoredMessages as $oldMessage) { - if (!isset($this->ignoredMessageIds[$oldMessage->folderid])) - $this->ignoredMessageIds[$oldMessage->folderid] = array(); - $this->ignoredMessageIds[$oldMessage->folderid][] = $oldMessage->id; - } - } - - $foundMessage = false; - // there are ignored messages in that folder - if (isset($this->ignoredMessageIds[$folderid])) { - // resync of a folder.. we should remove all previousily ignored messages - if ($id === false || in_array($id, $this->ignoredMessageIds[$folderid], true)) { - $foundMessage = true; - } - } - - return $foundMessage; - } - - /**---------------------------------------------------------------------------------------------------------- - * HierarchyCache and ContentData operations - */ - - /** - * Sets the HierarchyCache - * The hierarchydata, can be: - * - false a new HierarchyCache is initialized - * - array() new HierarchyCache is initialized and data from GetHierarchy is loaded - * - string previousely serialized data is loaded - * - * @param string $hierarchydata (opt) - * - * @access public - * @return boolean - */ - public function SetHierarchyCache($hierarchydata = false) { - if ($hierarchydata !== false && $hierarchydata instanceof ChangesMemoryWrapper) { - $this->hierarchyCache = $hierarchydata; - $this->hierarchyCache->CopyOldState(); - } - else - $this->hierarchyCache = new ChangesMemoryWrapper(); - - if (is_array($hierarchydata)) - return $this->hierarchyCache->ImportFolders($hierarchydata); - return true; - } - - /** - * Returns serialized data of the HierarchyCache - * - * @access public - * @return string - */ - public function GetHierarchyCacheData() { - if (isset($this->hierarchyCache)) - return $this->hierarchyCache; - - ZLog::Write(LOGLEVEL_WARN, "ASDevice->GetHierarchyCacheData() has no data! HierarchyCache probably never initialized."); - return false; - } - - /** - * Returns the HierarchyCache Object - * - * @access public - * @return object HierarchyCache - */ - public function GetHierarchyCache() { - if (!isset($this->hierarchyCache)) - $this->SetHierarchyCache(); - - ZLog::Write(LOGLEVEL_DEBUG, "ASDevice->GetHierarchyCache(): ". $this->hierarchyCache->GetStat()); - return $this->hierarchyCache; - } - - /** - * Returns all known folderids - * - * @access public - * @return array - */ - public function GetAllFolderIds() { - if (isset($this->contentData) && is_array($this->contentData)) - return array_keys($this->contentData); - return array(); - } - - /** - * Returns a linked UUID for a folder id - * - * @param string $folderid (opt) if not set, Hierarchy UUID is returned - * - * @access public - * @return string - */ - public function GetFolderUUID($folderid = false) { - if ($folderid === false) - return (isset($this->hierarchyUuid) && $this->hierarchyUuid !== self::UNDEFINED) ? $this->hierarchyUuid : false; - else if (isset($this->contentData) && isset($this->contentData[$folderid]) && isset($this->contentData[$folderid][self::FOLDERUUID])) - return $this->contentData[$folderid][self::FOLDERUUID]; - return false; - } - - /** - * Link a UUID to a folder id - * If a boolean false UUID is sent, the relation is removed - * - * @param string $uuid - * @param string $folderid (opt) if not set Hierarchy UUID is linked - * - * @access public - * @return boolean - */ - public function SetFolderUUID($uuid, $folderid = false) { - if ($folderid === false) { - $this->hierarchyUuid = $uuid; - // when unsetting the hierarchycache, also remove saved contentdata and ignoredmessages - if ($folderid === false) { - $this->contentData = array(); - $this->ignoredMessageIds = array(); - $this->ignoredMessages = array(); - } - } - else { - - $contentData = $this->contentData; - if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid])) - $contentData[$folderid] = array(); - - // check if the foldertype is set. This has to be available at this point, as generated during the first HierarchySync - if (!isset($contentData[$folderid][self::FOLDERTYPE])) - return false; - - if ($uuid) - $contentData[$folderid][self::FOLDERUUID] = $uuid; - else - $contentData[$folderid][self::FOLDERUUID] = false; - - $this->contentData = $contentData; - } - } - - /** - * Returns a foldertype for a folder already known to the mobile - * - * @param string $folderid - * - * @access public - * @return int/boolean returns false if the type is not set - */ - public function GetFolderType($folderid) { - if (isset($this->contentData) && isset($this->contentData[$folderid]) && - isset($this->contentData[$folderid][self::FOLDERTYPE]) ) - - return $this->contentData[$folderid][self::FOLDERTYPE]; - return false; - } - - /** - * Sets the foldertype of a folder id - * - * @param string $uuid - * @param string $folderid (opt) if not set Hierarchy UUID is linked - * - * @access public - * @return boolean true if the type was set or updated - */ - public function SetFolderType($folderid, $foldertype) { - $contentData = $this->contentData; - - if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid])) - $contentData[$folderid] = array(); - if (!isset($contentData[$folderid][self::FOLDERTYPE]) || $contentData[$folderid][self::FOLDERTYPE] != $foldertype ) { - $contentData[$folderid][self::FOLDERTYPE] = $foldertype; - $this->contentData = $contentData; - return true; - } - return false; - } - - /** - * Gets the supported fields transmitted previousely by the device - * for a certain folder - * - * @param string $folderid - * - * @access public - * @return array/boolean false means no supportedFields are available - */ - public function GetSupportedFields($folderid) { - if (isset($this->contentData) && isset($this->contentData[$folderid]) && - isset($this->contentData[$folderid][self::FOLDERUUID]) && $this->contentData[$folderid][self::FOLDERUUID] !== false && - isset($this->contentData[$folderid][self::FOLDERSUPPORTEDFIELDS]) ) - - return $this->contentData[$folderid][self::FOLDERSUPPORTEDFIELDS]; - - return false; - } - - /** - * Sets the set of supported fields transmitted by the device for a certain folder - * - * @param string $folderid - * @param array $fieldlist supported fields - * - * @access public - * @return boolean - */ - public function SetSupportedFields($folderid, $fieldlist) { - $contentData = $this->contentData; - if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid])) - $contentData[$folderid] = array(); - - $contentData[$folderid][self::FOLDERSUPPORTEDFIELDS] = $fieldlist; - $this->contentData = $contentData; - return true; - } - - /** - * Gets the current sync status of a certain folder - * - * @param string $folderid - * - * @access public - * @return mixed/boolean false means the status is not available - */ - public function GetFolderSyncStatus($folderid) { - if (isset($this->contentData) && isset($this->contentData[$folderid]) && - isset($this->contentData[$folderid][self::FOLDERUUID]) && $this->contentData[$folderid][self::FOLDERUUID] !== false && - isset($this->contentData[$folderid][self::FOLDERSYNCSTATUS]) ) - - return $this->contentData[$folderid][self::FOLDERSYNCSTATUS]; - - return false; - } - - /** - * Sets the current sync status of a certain folder - * - * @param string $folderid - * @param mixed $status if set to false the current status is deleted - * - * @access public - * @return boolean - */ - public function SetFolderSyncStatus($folderid, $status) { - $contentData = $this->contentData; - if (!isset($contentData[$folderid]) || !is_array($contentData[$folderid])) - $contentData[$folderid] = array(); - - if ($status !== false) { - $contentData[$folderid][self::FOLDERSYNCSTATUS] = $status; - } - else if (isset($contentData[$folderid][self::FOLDERSYNCSTATUS])) { - unset($contentData[$folderid][self::FOLDERSYNCSTATUS]); - } - - $this->contentData = $contentData; - return true; - } - -} diff --git a/sources/lib/core/bodypreference.php b/sources/lib/core/bodypreference.php deleted file mode 100644 index 364ae21..0000000 --- a/sources/lib/core/bodypreference.php +++ /dev/null @@ -1,67 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class BodyPreference extends StateObject { - protected $unsetdata = array( 'truncationsize' => false, - 'allornone' => false, - 'preview' => false, - ); - - /** - * expected magic getters and setters - * - * GetTruncationSize() + SetTruncationSize() - * GetAllOrNone() + SetAllOrNone() - * GetPreview() + SetPreview() - */ - - /** - * Indicates if this object has values - * - * @access public - * @return boolean - */ - public function HasValues() { - return (count($this->data) > 0); - } -} diff --git a/sources/lib/core/changesmemorywrapper.php b/sources/lib/core/changesmemorywrapper.php deleted file mode 100644 index ab2963f..0000000 --- a/sources/lib/core/changesmemorywrapper.php +++ /dev/null @@ -1,347 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class ChangesMemoryWrapper extends HierarchyCache implements IImportChanges, IExportChanges { - const CHANGE = 1; - const DELETION = 2; - - private $changes; - private $step; - private $destinationImporter; - private $exportImporter; - - /** - * Constructor - * - * @access public - * @return - */ - public function ChangesMemoryWrapper() { - $this->changes = array(); - $this->step = 0; - parent::HierarchyCache(); - } - - /** - * Only used to load additional folder sync information for hierarchy changes - * - * @param array $state current state of additional hierarchy folders - * - * @access public - * @return boolean - */ - public function Config($state, $flags = 0) { - // we should never forward this changes to a backend - if (!isset($this->destinationImporter)) { - foreach($state as $addKey => $addFolder) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : process folder '%s'", $addFolder->displayname)); - if (isset($addFolder->NoBackendFolder) && $addFolder->NoBackendFolder == true) { - $hasRights = ZPush::GetBackend()->Setup($addFolder->Store, true, $addFolder->serverid); - // delete the folder on the device - if (! $hasRights) { - // delete the folder only if it was an additional folder before, else ignore it - $synchedfolder = $this->GetFolder($addFolder->serverid); - if (isset($synchedfolder->NoBackendFolder) && $synchedfolder->NoBackendFolder == true) - $this->ImportFolderDeletion($addFolder->serverid, $addFolder->parentid); - continue; - } - } - // add folder to the device - if folder is already on the device, nothing will happen - $this->ImportFolderChange($addFolder); - } - - // look for folders which are currently on the device if there are now not to be synched anymore - $alreadyDeleted = $this->GetDeletedFolders(); - foreach ($this->ExportFolders(true) as $sid => $folder) { - // we are only looking at additional folders - if (isset($folder->NoBackendFolder)) { - // look if this folder is still in the list of additional folders and was not already deleted (e.g. missing permissions) - if (!array_key_exists($sid, $state) && !array_key_exists($sid, $alreadyDeleted)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ChangesMemoryWrapper->Config(AdditionalFolders) : previously synchronized folder '%s' is not to be synched anymore. Sending delete to mobile.", $folder->displayname)); - $this->ImportFolderDeletion($folder->serverid, $folder->parentid); - } - } - } - } - return true; - } - - - /** - * Implement interfaces which are never used - */ - public function GetState() { return false;} - public function LoadConflicts($contentparameters, $state) { return true; } - public function ConfigContentParameters($contentparameters) { return true; } - public function ImportMessageReadFlag($id, $flags) { return true; } - public function ImportMessageMove($id, $newfolder) { return true; } - - /**---------------------------------------------------------------------------------------------------------- - * IImportChanges & destination importer - */ - - /** - * Sets an importer where incoming changes should be sent to - * - * @param IImportChanges $importer message to be changed - * - * @access public - * @return boolean - */ - public function SetDestinationImporter(&$importer) { - $this->destinationImporter = $importer; - } - - /** - * Imports a message change, which is imported into memory - * - * @param string $id id of message which is changed - * @param SyncObject $message message to be changed - * - * @access public - * @return boolean - */ - public function ImportMessageChange($id, $message) { - $this->changes[] = array(self::CHANGE, $id); - return true; - } - - /** - * Imports a message deletion, which is imported into memory - * - * @param string $id id of message which is deleted - * - * @access public - * @return boolean - */ - public function ImportMessageDeletion($id) { - $this->changes[] = array(self::DELETION, $id); - return true; - } - - /** - * Checks if a message id is flagged as changed - * - * @param string $id message id - * - * @access public - * @return boolean - */ - public function IsChanged($id) { - return (array_search(array(self::CHANGE, $id), $this->changes) === false) ? false:true; - } - - /** - * Checks if a message id is flagged as deleted - * - * @param string $id message id - * - * @access public - * @return boolean - */ - public function IsDeleted($id) { - return (array_search(array(self::DELETION, $id), $this->changes) === false) ? false:true; - } - - /** - * Imports a folder change - * - * @param SyncFolder $folder folder to be changed - * - * @access public - * @return boolean - */ - public function ImportFolderChange($folder) { - // if the destinationImporter is set, then this folder should be processed by another importer - // instead of being loaded in memory. - if (isset($this->destinationImporter)) { - // normally the $folder->type is not set, but we need this value to check if the change operation is permitted - // e.g. system folders can normally not be changed - set the type from cache and let the destinationImporter decide - if (!isset($folder->type)) { - $cacheFolder = $this->GetFolder($folder->serverid); - $folder->type = $cacheFolder->type; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Set foldertype for folder '%s' from cache as it was not sent: '%s'", $folder->displayname, $folder->type)); - } - - $ret = $this->destinationImporter->ImportFolderChange($folder); - - // if the operation was sucessfull, update the HierarchyCache - if ($ret) { - // for folder creation, the serverid is not set and has to be updated before - if (!isset($folder->serverid) || $folder->serverid == "") - $folder->serverid = $ret; - - $this->AddFolder($folder); - } - return $ret; - } - // load into memory - else { - if (isset($folder->serverid)) { - // The Zarafa HierarchyExporter exports all kinds of changes for folders (e.g. update no. of unread messages in a folder). - // These changes are not relevant for the mobiles, as something changes but the relevant displayname and parentid - // stay the same. These changes will be dropped and are not sent! - $cacheFolder = $this->GetFolder($folder->serverid); - if ($folder->equals($this->GetFolder($folder->serverid))) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ChangesMemoryWrapper->ImportFolderChange(): Change for folder '%s' will not be sent as modification is not relevant.", $folder->displayname)); - return false; - } - - // load this change into memory - $this->changes[] = array(self::CHANGE, $folder); - - // HierarchyCache: already add/update the folder so changes are not sent twice (if exported twice) - $this->AddFolder($folder); - return true; - } - return false; - } - } - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent (opt) the parent id of the folders - * - * @access public - * @return boolean - */ - public function ImportFolderDeletion($id, $parent = false) { - // if the forwarder is set, then this folder should be processed by another importer - // instead of being loaded in mem. - if (isset($this->destinationImporter)) { - $ret = $this->destinationImporter->ImportFolderDeletion($id, $parent); - - // if the operation was sucessfull, update the HierarchyCache - if ($ret) - $this->DelFolder($id); - - return $ret; - } - else { - // if this folder is not in the cache, the change does not need to be streamed to the mobile - if ($this->GetFolder($id)) { - - // load this change into memory - $this->changes[] = array(self::DELETION, $id, $parent); - - // HierarchyCache: delete the folder so changes are not sent twice (if exported twice) - $this->DelFolder($id); - return true; - } - } - } - - - /**---------------------------------------------------------------------------------------------------------- - * IExportChanges & destination importer - */ - - /** - * Initializes the Exporter where changes are synchronized to - * - * @param IImportChanges $importer - * - * @access public - * @return boolean - */ - public function InitializeExporter(&$importer) { - $this->exportImporter = $importer; - $this->step = 0; - return true; - } - - /** - * Returns the amount of changes to be exported - * - * @access public - * @return int - */ - public function GetChangeCount() { - return count($this->changes); - } - - /** - * Synchronizes a change. Only HierarchyChanges will be Synchronized() - * - * @access public - * @return array - */ - public function Synchronize() { - if($this->step < count($this->changes) && isset($this->exportImporter)) { - - $change = $this->changes[$this->step]; - - if ($change[0] == self::CHANGE) { - if (! $this->GetFolder($change[1]->serverid, true)) - $change[1]->flags = SYNC_NEWMESSAGE; - - $this->exportImporter->ImportFolderChange($change[1]); - } - // deletion - else { - $this->exportImporter->ImportFolderDeletion($change[1], $change[2]); - } - $this->step++; - - // return progress array - return array("steps" => count($this->changes), "progress" => $this->step); - } - else - return false; - } - - /** - * Initializes a few instance variables - * called after unserialization - * - * @access public - * @return array - */ - public function __wakeup() { - $this->changes = array(); - $this->step = 0; - } -} diff --git a/sources/lib/core/contentparameters.php b/sources/lib/core/contentparameters.php deleted file mode 100644 index d3cee6e..0000000 --- a/sources/lib/core/contentparameters.php +++ /dev/null @@ -1,139 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class ContentParameters extends StateObject { - protected $unsetdata = array( 'contentclass' => false, - 'foldertype' => '', - 'conflict' => false, - 'deletesasmoves' => true, - 'filtertype' => false, - 'truncation' => false, - 'rtftruncation' => false, - 'mimesupport' => false, - 'conversationmode' => false, - ); - - private $synckeyChanged = false; - - /** - * Expected magic getters and setters - * - * GetContentClass() + SetContentClass() - * GetConflict() + SetConflict() - * GetDeletesAsMoves() + SetDeletesAsMoves() - * GetFilterType() + SetFilterType() - * GetTruncation() + SetTruncation - * GetRTFTruncation() + SetRTFTruncation() - * GetMimeSupport () + SetMimeSupport() - * GetMimeTruncation() + SetMimeTruncation() - * GetConversationMode() + SetConversationMode() - */ - - /** - * Overwrite StateObject->__call so we are able to handle ContentParameters->BodyPreference() - * - * @access public - * @return mixed - */ - public function __call($name, $arguments) { - if ($name === "BodyPreference") - return $this->BodyPreference($arguments[0]); - - return parent::__call($name, $arguments); - } - - - /** - * Instantiates/returns the bodypreference object for a type - * - * @param int $type - * - * @access public - * @return int/boolean returns false if value is not defined - */ - public function BodyPreference($type) { - if (!isset($this->bodypref)) - $this->bodypref = array(); - - if (isset($this->bodypref[$type])) - return $this->bodypref[$type]; - else { - $asb = new BodyPreference(); - $arr = (array)$this->bodypref; - $arr[$type] = $asb; - $this->bodypref = $arr; - return $asb; - } - } - - /** - * Returns available body preference objects - * - * @access public - * @return array/boolean returns false if the client's body preference is not available - */ - public function GetBodyPreference() { - if (!isset($this->bodypref) || !(is_array($this->bodypref) || empty($this->bodypref))) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ContentParameters->GetBodyPreference(): bodypref is empty or not set")); - return false; - } - return array_keys($this->bodypref); - } - - /** - * Called before the StateObject is serialized - * - * @access protected - * @return boolean - */ - protected function preSerialize() { - parent::preSerialize(); - - if ($this->changed === true && $this->synckeyChanged) - $this->lastsynctime = time(); - - return true; - } -} diff --git a/sources/lib/core/devicemanager.php b/sources/lib/core/devicemanager.php deleted file mode 100644 index fe1a5e5..0000000 --- a/sources/lib/core/devicemanager.php +++ /dev/null @@ -1,903 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class DeviceManager { - // broken message indicators - const MSG_BROKEN_UNKNOWN = 1; - const MSG_BROKEN_CAUSINGLOOP = 2; - const MSG_BROKEN_SEMANTICERR = 4; - - const FLD_SYNC_INITIALIZED = 1; - const FLD_SYNC_INPROGRESS = 2; - const FLD_SYNC_COMPLETED = 4; - - private $device; - private $deviceHash; - private $statemachine; - private $stateManager; - private $incomingData = 0; - private $outgoingData = 0; - - private $windowSize; - private $latestFolder; - - private $loopdetection; - private $hierarchySyncRequired; - - /** - * Constructor - * - * @access public - */ - public function DeviceManager() { - $this->statemachine = ZPush::GetStateMachine(); - $this->deviceHash = false; - $this->devid = Request::GetDeviceID(); - $this->windowSize = array(); - $this->latestFolder = false; - $this->hierarchySyncRequired = false; - - // only continue if deviceid is set - if ($this->devid) { - $this->device = new ASDevice($this->devid, Request::GetDeviceType(), Request::GetGETUser(), Request::GetUserAgent()); - $this->loadDeviceData(); - - ZPush::GetTopCollector()->SetUserAgent($this->device->GetDeviceUserAgent()); - } - else - throw new FatalNotImplementedException("Can not proceed without a device id."); - - $this->loopdetection = ZPush::GetLoopDetection(); - $this->loopdetection->ProcessLoopDetectionInit(); - $this->loopdetection->ProcessLoopDetectionPreviousConnectionFailed(); - - $this->stateManager = new StateManager(); - $this->stateManager->SetDevice($this->device); - } - - /** - * Returns the StateManager for the current device - * - * @access public - * @return StateManager - */ - public function GetStateManager() { - return $this->stateManager; - } - - /**---------------------------------------------------------------------------------------------------------- - * Device operations - */ - - /** - * Announces amount of transmitted data to the DeviceManager - * - * @param int $datacounter - * - * @access public - * @return boolean - */ - public function SentData($datacounter) { - // TODO save this somewhere - $this->incomingData = Request::GetContentLength(); - $this->outgoingData = $datacounter; - } - - /** - * Called at the end of the request - * Statistics about received/sent data is saved here - * - * @access public - * @return boolean - */ - public function Save() { - // TODO save other stuff - - // check if previousily ignored messages were synchronized for the current folder - // on multifolder operations of AS14 this is done by setLatestFolder() - if ($this->latestFolder !== false) - $this->checkBrokenMessages($this->latestFolder); - - // update the user agent and AS version on the device - $this->device->SetUserAgent(Request::GetUserAgent()); - $this->device->SetASVersion(Request::GetProtocolVersion()); - - // data to be saved - $data = $this->device->GetData(); - if ($data && Request::IsValidDeviceID()) { - ZLog::Write(LOGLEVEL_DEBUG, "DeviceManager->Save(): Device data changed"); - - try { - // check if this is the first time the device data is saved and it is authenticated. If so, link the user to the device id - if ($this->device->IsNewDevice() && RequestProcessor::isUserAuthenticated()) { - ZLog::Write(LOGLEVEL_INFO, sprintf("Linking device ID '%s' to user '%s'", $this->devid, $this->device->GetDeviceUser())); - $this->statemachine->LinkUserDevice($this->device->GetDeviceUser(), $this->devid); - } - - if (RequestProcessor::isUserAuthenticated() || $this->device->GetForceSave() ) { - $this->statemachine->SetState($data, $this->devid, IStateMachine::DEVICEDATA); - ZLog::Write(LOGLEVEL_DEBUG, "DeviceManager->Save(): Device data saved"); - } - } - catch (StateNotFoundException $snfex) { - ZLog::Write(LOGLEVEL_ERROR, "DeviceManager->Save(): Exception: ". $snfex->getMessage()); - } - } - - // remove old search data - $oldpid = $this->loopdetection->ProcessLoopDetectionGetOutdatedSearchPID(); - if ($oldpid) { - ZPush::GetBackend()->GetSearchProvider()->TerminateSearch($oldpid); - } - - // we terminated this process - if ($this->loopdetection) - $this->loopdetection->ProcessLoopDetectionTerminate(); - - return true; - } - - /** - * Newer mobiles send extensive device informations with the Settings command - * These informations are saved in the ASDevice - * - * @param SyncDeviceInformation $deviceinformation - * - * @access public - * @return boolean - */ - public function SaveDeviceInformation($deviceinformation) { - ZLog::Write(LOGLEVEL_DEBUG, "Saving submitted device information"); - - // set the user agent - if (isset($deviceinformation->useragent)) - $this->device->SetUserAgent($deviceinformation->useragent); - - // save other informations - foreach (array("model", "imei", "friendlyname", "os", "oslanguage", "phonenumber", "mobileoperator", "enableoutboundsms") as $info) { - if (isset($deviceinformation->$info) && $deviceinformation->$info != "") { - $this->device->__set("device".$info, $deviceinformation->$info); - } - } - return true; - } - - /**---------------------------------------------------------------------------------------------------------- - * Provisioning operations - */ - - /** - * Checks if the sent policykey matches the latest policykey - * saved for the device - * - * @param string $policykey - * @param boolean $noDebug (opt) by default, debug message is shown - * - * @access public - * @return boolean - */ - public function ProvisioningRequired($policykey, $noDebug = false) { - $this->loadDeviceData(); - - // check if a remote wipe is required - if ($this->device->GetWipeStatus() > SYNC_PROVISION_RWSTATUS_OK) { - ZLog::Write(LOGLEVEL_INFO, sprintf("DeviceManager->ProvisioningRequired('%s'): YES, remote wipe requested", $policykey)); - return true; - } - - $p = ( ($this->device->GetWipeStatus() != SYNC_PROVISION_RWSTATUS_NA && $policykey != $this->device->GetPolicyKey()) || - Request::WasPolicyKeySent() && $this->device->GetPolicyKey() == ASDevice::UNDEFINED ); - if (!$noDebug || $p) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->ProvisioningRequired('%s') saved device key '%s': %s", $policykey, $this->device->GetPolicyKey(), Utils::PrintAsString($p))); - return $p; - } - - /** - * Generates a new Policykey - * - * @access public - * @return int - */ - public function GenerateProvisioningPolicyKey() { - return mt_rand(100000000, 999999999); - } - - /** - * Attributes a provisioned policykey to a device - * - * @param int $policykey - * - * @access public - * @return boolean status - */ - public function SetProvisioningPolicyKey($policykey) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->SetPolicyKey('%s')", $policykey)); - return $this->device->SetPolicyKey($policykey); - } - - /** - * Builds a Provisioning SyncObject with policies - * - * @access public - * @return SyncProvisioning - */ - public function GetProvisioningObject() { - $p = new SyncProvisioning(); - // TODO load systemwide Policies - $p->Load($this->device->GetPolicies()); - return $p; - } - - /** - * Returns the status of the remote wipe policy - * - * @access public - * @return int returns the current status of the device - SYNC_PROVISION_RWSTATUS_* - */ - public function GetProvisioningWipeStatus() { - return $this->device->GetWipeStatus(); - } - - /** - * Updates the status of the remote wipe - * - * @param int $status - SYNC_PROVISION_RWSTATUS_* - * - * @access public - * @return boolean could fail if trying to update status to a wipe status which was not requested before - */ - public function SetProvisioningWipeStatus($status) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->SetProvisioningWipeStatus() change from '%d' to '%d'",$this->device->GetWipeStatus(), $status)); - - if ($status > SYNC_PROVISION_RWSTATUS_OK && !($this->device->GetWipeStatus() > SYNC_PROVISION_RWSTATUS_OK)) { - ZLog::Write(LOGLEVEL_ERROR, "Not permitted to update remote wipe status to a higher value as remote wipe was not initiated!"); - return false; - } - $this->device->SetWipeStatus($status); - return true; - } - - - /**---------------------------------------------------------------------------------------------------------- - * LEGACY AS 1.0 and WRAPPER operations - */ - - /** - * Returns a wrapped Importer & Exporter to use the - * HierarchyChache - * - * @see ChangesMemoryWrapper - * @access public - * @return object HierarchyCache - */ - public function GetHierarchyChangesWrapper() { - return $this->device->GetHierarchyCache(); - } - - /** - * Initializes the HierarchyCache for legacy syncs - * this is for AS 1.0 compatibility: - * save folder information synched with GetHierarchy() - * - * @param string $folders Array with folder information - * - * @access public - * @return boolean - */ - public function InitializeFolderCache($folders) { - $this->stateManager->SetDevice($this->device); - return $this->stateManager->InitializeFolderCache($folders); - } - - /** - * Returns the ActiveSync folder type for a FolderID - * - * @param string $folderid - * - * @access public - * @return int/boolean boolean if no type is found - */ - public function GetFolderTypeFromCacheById($folderid) { - return $this->device->GetFolderType($folderid); - } - - /** - * Returns a FolderID of default classes - * this is for AS 1.0 compatibility: - * this information was made available during GetHierarchy() - * - * @param string $class The class requested - * - * @access public - * @return string - * @throws NoHierarchyCacheAvailableException - */ - public function GetFolderIdFromCacheByClass($class) { - $folderidforClass = false; - // look at the default foldertype for this class - $type = ZPush::getDefaultFolderTypeFromFolderClass($class); - - if ($type && $type > SYNC_FOLDER_TYPE_OTHER && $type < SYNC_FOLDER_TYPE_USER_MAIL) { - $folderids = $this->device->GetAllFolderIds(); - foreach ($folderids as $folderid) { - if ($type == $this->device->GetFolderType($folderid)) { - $folderidforClass = $folderid; - break; - } - } - - // Old Palm Treos always do initial sync for calendar and contacts, even if they are not made available by the backend. - // We need to fake these folderids, allowing a fake sync/ping, even if they are not supported by the backend - // if the folderid would be available, they would already be returned in the above statement - if ($folderidforClass == false && ($type == SYNC_FOLDER_TYPE_APPOINTMENT || $type == SYNC_FOLDER_TYPE_CONTACT)) - $folderidforClass = SYNC_FOLDER_TYPE_DUMMY; - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->GetFolderIdFromCacheByClass('%s'): '%s' => '%s'", $class, $type, $folderidforClass)); - return $folderidforClass; - } - - /** - * Returns a FolderClass for a FolderID which is known to the mobile - * - * @param string $folderid - * - * @access public - * @return int - * @throws NoHierarchyCacheAvailableException, NotImplementedException - */ - public function GetFolderClassFromCacheByID($folderid) { - //TODO check if the parent folder exists and is also beeing synchronized - $typeFromCache = $this->device->GetFolderType($folderid); - if ($typeFromCache === false) - throw new NoHierarchyCacheAvailableException(sprintf("Folderid '%s' is not fully synchronized on the device", $folderid)); - - $class = ZPush::GetFolderClassFromFolderType($typeFromCache); - if ($class === false) - throw new NotImplementedException(sprintf("Folderid '%s' is saved to be of type '%d' but this type is not implemented", $folderid, $typeFromCache)); - - return $class; - } - - /** - * Checks if the message should be streamed to a mobile - * Should always be called before a message is sent to the mobile - * Returns true if there is something wrong and the content could break the - * synchronization - * - * @param string $id message id - * @param SyncObject &$message the method could edit the message to change the flags - * - * @access public - * @return boolean returns true if the message should NOT be send! - */ - public function DoNotStreamMessage($id, &$message) { - $folderid = $this->getLatestFolder(); - - if (isset($message->parentid)) - $folder = $message->parentid; - - // message was identified to be causing a loop - if ($this->loopdetection->IgnoreNextMessage(true, $id, $folderid)) { - $this->AnnounceIgnoredMessage($folderid, $id, $message, self::MSG_BROKEN_CAUSINGLOOP); - return true; - } - - if (!is_object($message)) - throw new Exception("DeviceManager->DoNotStreamMessage(): message isn't an object"); - // message is semantically incorrect - if (!$message->Check(true)) { - $this->AnnounceIgnoredMessage($folderid, $id, $message, self::MSG_BROKEN_SEMANTICERR); - return true; - } - - // check if this message is broken - if ($this->device->HasIgnoredMessage($folderid, $id)) { - // reset the flags so the message is always streamed with - $message->flags = false; - - // track the broken message in the loop detection - $this->loopdetection->SetBrokenMessage($folderid, $id); - } - return false; - } - - /** - * Removes device information about a broken message as it is been removed from the mobile. - * - * @param string $id message id - * - * @access public - * @return boolean - */ - public function RemoveBrokenMessage($id) { - $folderid = $this->getLatestFolder(); - if ($this->device->RemoveIgnoredMessage($folderid, $id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("DeviceManager->RemoveBrokenMessage('%s', '%s'): cleared data about previously ignored message", $folderid, $id)); - return true; - } - return false; - } - - /** - * Amount of items to me synchronized - * - * @param string $folderid - * @param string $type - * @param int $queuedmessages; - * @access public - * @return int - */ - public function GetWindowSize($folderid, $type, $uuid, $statecounter, $queuedmessages) { - if (isset($this->windowSize[$folderid])) - $items = $this->windowSize[$folderid]; - else - $items = (defined("SYNC_MAX_ITEMS")) ? SYNC_MAX_ITEMS : 100; - - if (defined("SYNC_MAX_ITEMS") && SYNC_MAX_ITEMS < $items) { - if ($queuedmessages > SYNC_MAX_ITEMS) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->GetWindowSize() overwriting max items requested of %d by %d forced in configuration.", $items, SYNC_MAX_ITEMS)); - $items = SYNC_MAX_ITEMS; - } - - $this->setLatestFolder($folderid); - - // detect if this is a loop condition - if ($this->loopdetection->Detect($folderid, $type, $uuid, $statecounter, $items, $queuedmessages)) - $items = ($items == 0) ? 0: 1+($this->loopdetection->IgnoreNextMessage(false)?1:0) ; - - if ($items >= 0 && $items <= 2) - ZLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Messages sent to the mobile will be restricted to %d items in order to identify the conflict", $items)); - - return $items; - } - - /** - * Sets the amount of items the device is requesting - * - * @param string $folderid - * @param int $maxItems - * - * @access public - * @return boolean - */ - public function SetWindowSize($folderid, $maxItems) { - $this->windowSize[$folderid] = $maxItems; - - return true; - } - - /** - * Sets the supported fields transmitted by the device for a certain folder - * - * @param string $folderid - * @param array $fieldlist supported fields - * - * @access public - * @return boolean - */ - public function SetSupportedFields($folderid, $fieldlist) { - return $this->device->SetSupportedFields($folderid, $fieldlist); - } - - /** - * Gets the supported fields transmitted previousely by the device - * for a certain folder - * - * @param string $folderid - * - * @access public - * @return array/boolean - */ - public function GetSupportedFields($folderid) { - return $this->device->GetSupportedFields($folderid); - } - - /** - * Removes all linked states of a specific folder. - * During next request the folder is resynchronized. - * - * @param string $folderid - * - * @access public - * @return boolean - */ - public function ForceFolderResync($folderid) { - ZLog::Write(LOGLEVEL_INFO, sprintf("DeviceManager->ForceFolderResync('%s'): folder resync", $folderid)); - - // delete folder states - StateManager::UnLinkState($this->device, $folderid); - - return true; - } - - /** - * Removes all linked states from a device. - * During next requests a full resync is triggered. - * - * @access public - * @return boolean - */ - public function ForceFullResync() { - ZLog::Write(LOGLEVEL_INFO, "Full device resync requested"); - - // delete hierarchy states - StateManager::UnLinkState($this->device, false); - - // delete all other uuids - foreach ($this->device->GetAllFolderIds() as $folderid) - $uuid = StateManager::UnLinkState($this->device, $folderid); - - return true; - } - - /** - * Indicates if the hierarchy should be resynchronized - * e.g. during PING - * - * @access public - * @return boolean - */ - public function IsHierarchySyncRequired() { - // check if a hierarchy sync might be necessary - if ($this->device->GetFolderUUID(false) === false) - $this->hierarchySyncRequired = true; - - return $this->hierarchySyncRequired; - } - - /** - * Indicates if a full hierarchy resync should be triggered due to loops - * - * @access public - * @return boolean - */ - public function IsHierarchyFullResyncRequired() { - // do not check for loop detection, if the foldersync is not yet complete - if ($this->GetFolderSyncComplete() === false) { - ZLog::Write(LOGLEVEL_INFO, "DeviceManager->IsHierarchyFullResyncRequired(): aborted, as exporting of folders has not yet completed"); - return false; - } - // check for potential process loops like described in ZP-5 - return $this->loopdetection->ProcessLoopDetectionIsHierarchyResyncRequired(); - } - - /** - * Adds an Exceptions to the process tracking - * - * @param Exception $exception - * - * @access public - * @return boolean - */ - public function AnnounceProcessException($exception) { - return $this->loopdetection->ProcessLoopDetectionAddException($exception); - } - - /** - * Adds a non-ok status for a folderid to the process tracking. - * On 'false' a hierarchy status is assumed - * - * @access public - * @return boolean - */ - public function AnnounceProcessStatus($folderid, $status) { - return $this->loopdetection->ProcessLoopDetectionAddStatus($folderid, $status); - } - - /** - * Announces that the current process is a push connection to the process loop - * detection and to the Top collector - * - * @access public - * @return boolean - */ - public function AnnounceProcessAsPush() { - ZLog::Write(LOGLEVEL_DEBUG, "Announce process as PUSH connection"); - - return $this->loopdetection->ProcessLoopDetectionSetAsPush() && ZPush::GetTopCollector()->SetAsPushConnection(); - } - - /** - * Checks if the given counter for a certain uuid+folderid was already exported or modified. - * This is called when a heartbeat request found changes to make sure that the same - * changes are not exported twice, as during the heartbeat there could have been a normal - * sync request. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean indicating if an uuid+counter were exported (with changes) before - */ - public function CheckHearbeatStateIntegrity($folderid, $uuid, $counter) { - return $this->loopdetection->IsSyncStateObsolete($folderid, $uuid, $counter); - } - - /** - * Marks a syncstate as obsolete for Heartbeat, as e.g. an import was started using it. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return - */ - public function SetHeartbeatStateIntegrity($folderid, $uuid, $counter) { - return $this->loopdetection->SetSyncStateUsage($folderid, $uuid, $counter); - } - - /** - * Sets the current status of the folder - * - * @param string $folderid folder id - * @param int $statusflag current status: DeviceManager::FLD_SYNC_INITIALIZED, DeviceManager::FLD_SYNC_INPROGRESS, DeviceManager::FLD_SYNC_COMPLETED - * - * @access public - * @return - */ - public function SetFolderSyncStatus($folderid, $statusflag) { - $currentStatus = $this->device->GetFolderSyncStatus($folderid); - - // status available or just initialized - if (isset($currentStatus[ASDevice::FOLDERSYNCSTATUS]) || $statusflag == self::FLD_SYNC_INITIALIZED) { - // only update if there is a change - if ($statusflag !== $currentStatus[ASDevice::FOLDERSYNCSTATUS] && $statusflag != self::FLD_SYNC_COMPLETED) { - $this->device->SetFolderSyncStatus($folderid, array(ASDevice::FOLDERSYNCSTATUS => $statusflag)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SetFolderSyncStatus(): set %s for %s", $statusflag, $folderid)); - } - // if completed, remove the status - else if ($statusflag == self::FLD_SYNC_COMPLETED) { - $this->device->SetFolderSyncStatus($folderid, false); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SetFolderSyncStatus(): completed for %s", $folderid)); - } - } - - return true; - } - - /** - * Returns the indicator if the FolderSync was completed successfully (all folders synchronized) - * - * @access public - * @return boolean - */ - public function GetFolderSyncComplete() { - return $this->device->GetFolderSyncComplete(); - } - - /** - * Sets if the FolderSync was completed successfully (all folders synchronized) - * - * @param boolean $complete indicating if all folders were sent - * - * @access public - * @return boolean - */ - public function SetFolderSyncComplete($complete, $user = false, $devid = false) { - $this->device->SetFolderSyncComplete($complete); - } - - /** - * Removes the Loop detection data for a user & device - * - * @param string $user - * @param string $devid - * - * @access public - * @return boolean - */ - public function ClearLoopDetectionData($user, $devid) { - if ($user == false || $devid == false) { - return false; - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DeviceManager->ClearLoopDetectionData(): clearing data for user '%s' and device '%s'", $user, $devid)); - return $this->loopdetection->ClearData($user, $devid); - } - - /** - * Indicates if the device needs an AS version update - * - * @access public - * @return boolean - */ - public function AnnounceASVersion() { - $latest = ZPush::GetSupportedASVersion(); - $announced = $this->device->GetAnnouncedASversion(); - $this->device->SetAnnouncedASversion($latest); - - return ($announced != $latest); - } - - - /**---------------------------------------------------------------------------------------------------------- - * DeviceManager User-Device pre-authorization - */ - - /** - * Return if the User-Device has permission to sync against this Z-Push. - * - * @param string $user Username - * @param string $devid DeviceId - * - * @access public - * @return integer - */ - public function GetUserDevicePermission($user, $devid) { - return $this->statemachine->GetUserDevicePermission($user, $devid); - } - - /**---------------------------------------------------------------------------------------------------------- - * private DeviceManager methods - */ - - /** - * Loads devicedata from the StateMachine and loads it into the device - * - * @access public - * @return boolean - */ - private function loadDeviceData() { - if (!Request::IsValidDeviceID()) - return false; - try { - $deviceHash = $this->statemachine->GetStateHash($this->devid, IStateMachine::DEVICEDATA); - if ($deviceHash != $this->deviceHash) { - if ($this->deviceHash) - ZLog::Write(LOGLEVEL_DEBUG, "DeviceManager->loadDeviceData(): Device data was changed, reloading"); - $this->device->SetData($this->statemachine->GetState($this->devid, IStateMachine::DEVICEDATA)); - $this->deviceHash = $deviceHash; - } - } - catch (StateNotFoundException $snfex) { - $this->hierarchySyncRequired = true; - } - return true; - } - - /** - * Called when a SyncObject is not being streamed to the mobile. - * The user can be informed so he knows about this issue - * - * @param string $folderid id of the parent folder (may be false if unknown) - * @param string $id message id - * @param SyncObject $message the broken message - * @param string $reason (self::MSG_BROKEN_UNKNOWN, self::MSG_BROKEN_CAUSINGLOOP, self::MSG_BROKEN_SEMANTICERR) - * - * @access public - * @return boolean - */ - public function AnnounceIgnoredMessage($folderid, $id, SyncObject $message, $reason = self::MSG_BROKEN_UNKNOWN) { - if ($folderid === false) - $folderid = $this->getLatestFolder(); - - $class = get_class($message); - - $brokenMessage = new StateObject(); - $brokenMessage->id = $id; - $brokenMessage->folderid = $folderid; - $brokenMessage->ASClass = $class; - $brokenMessage->folderid = $folderid; - $brokenMessage->reasonCode = $reason; - $brokenMessage->reasonString = 'unknown cause'; - $brokenMessage->timestamp = time(); - $brokenMessage->asobject = $message; - $brokenMessage->reasonString = ZLog::GetLastMessage(LOGLEVEL_WARN); - - $this->device->AddIgnoredMessage($brokenMessage); - - ZLog::Write(LOGLEVEL_ERROR, sprintf("Ignored broken message (%s). Reason: '%s' Folderid: '%s' message id '%s'", $class, $reason, $folderid, $id)); - return true; - } - - /** - * Called when a SyncObject was streamed to the mobile. - * If the message could not be sent before this data is obsolete - * - * @param string $folderid id of the parent folder - * @param string $id message id - * - * @access public - * @return boolean returns true if the message was ignored before - */ - private function announceAcceptedMessage($folderid, $id) { - if ($this->device->RemoveIgnoredMessage($folderid, $id)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("DeviceManager->announceAcceptedMessage('%s', '%s'): cleared previously ignored message as message is sucessfully streamed",$folderid, $id)); - return true; - } - return false; - } - - /** - * Checks if there were broken messages streamed to the mobile. - * If the sync completes/continues without further erros they are marked as accepted - * - * @param string $folderid folderid which is to be checked - * - * @access private - * @return boolean - */ - private function checkBrokenMessages($folderid) { - // check for correctly synchronized messages of the folder - foreach($this->loopdetection->GetSyncedButBeforeIgnoredMessages($folderid) as $okID) { - $this->announceAcceptedMessage($folderid, $okID); - } - return true; - } - - /** - * Setter for the latest folder id - * on multi-folder operations of AS 14 this is used to set the new current folder id - * - * @param string $folderid the current folder - * - * @access private - * @return boolean - */ - private function setLatestFolder($folderid) { - // this is a multi folder operation - // check on ignoredmessages before discaring the folderid - if ($this->latestFolder !== false) - $this->checkBrokenMessages($this->latestFolder); - - $this->latestFolder = $folderid; - - return true; - } - - /** - * Getter for the latest folder id - * - * @access private - * @return string $folderid the current folder - */ - private function getLatestFolder() { - return $this->latestFolder; - } -} diff --git a/sources/lib/core/hierarchycache.php b/sources/lib/core/hierarchycache.php deleted file mode 100644 index 139a124..0000000 --- a/sources/lib/core/hierarchycache.php +++ /dev/null @@ -1,214 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class HierarchyCache { - private $changed = false; - protected $cacheById; - private $cacheByIdOld; - - /** - * Constructor of the HierarchyCache - * - * @access public - * @return - */ - public function HierarchyCache() { - $this->cacheById = array(); - $this->cacheByIdOld = $this->cacheById; - $this->changed = true; - } - - /** - * Indicates if the cache was changed - * - * @access public - * @return boolean - */ - public function IsStateChanged() { - return $this->changed; - } - - /** - * Copy current CacheById to memory - * - * @access public - * @return boolean - */ - public function CopyOldState() { - $this->cacheByIdOld = $this->cacheById; - return true; - } - - /** - * Returns the SyncFolder object for a folder id - * If $oldstate is set, then the data from the previous state is returned - * - * @param string $serverid - * @param boolean $oldstate (optional) by default false - * - * @access public - * @return SyncObject/boolean false if not found - */ - public function GetFolder($serverid, $oldState = false) { - if (!$oldState && array_key_exists($serverid, $this->cacheById)) { - return $this->cacheById[$serverid]; - } - else if ($oldState && array_key_exists($serverid, $this->cacheByIdOld)) { - return $this->cacheByIdOld[$serverid]; - } - return false; - } - - /** - * Adds a folder to the HierarchyCache - * - * @param SyncObject $folder - * - * @access public - * @return boolean - */ - public function AddFolder($folder) { - ZLog::Write(LOGLEVEL_DEBUG, "HierarchyCache: AddFolder() serverid: {$folder->serverid} displayname: {$folder->displayname}"); - - // on update the $folder does most of the times not contain a type - // we copy the value in this case to the new $folder object - if (isset($this->cacheById[$folder->serverid]) && (!isset($folder->type) || $folder->type == false) && isset($this->cacheById[$folder->serverid]->type)) { - $folder->type = $this->cacheById[$folder->serverid]->type; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HierarchyCache: AddFolder() is an update: used type '%s' from old object", $folder->type)); - } - - // add/update - $this->cacheById[$folder->serverid] = $folder; - $this->changed = true; - - return true; - } - - /** - * Removes a folder to the HierarchyCache - * - * @param string $serverid id of folder to be removed - * - * @access public - * @return boolean - */ - public function DelFolder($serverid) { - $ftype = $this->GetFolder($serverid); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HierarchyCache: DelFolder() serverid: '%s' - type: '%s'", $serverid, $ftype->type)); - unset($this->cacheById[$serverid]); - $this->changed = true; - return true; - } - - /** - * Imports a folder array to the HierarchyCache - * - * @param array $folders folders to the HierarchyCache - * - * @access public - * @return boolean - */ - public function ImportFolders($folders) { - if (!is_array($folders)) - return false; - - $this->cacheById = array(); - - foreach ($folders as $folder) { - if (!isset($folder->type)) - continue; - $this->AddFolder($folder); - } - return true; - } - - /** - * Exports all folders from the HierarchyCache - * - * @param boolean $oldstate (optional) by default false - * - * @access public - * @return array - */ - public function ExportFolders($oldstate = false) { - if ($oldstate === false) - return $this->cacheById; - else - return $this->cacheByIdOld; - } - - /** - * Returns all folder objects which were deleted in this operation - * - * @access public - * @return array with SyncFolder objects - */ - public function GetDeletedFolders() { - // diffing the OldCacheById with CacheById we know if folders were deleted - return array_diff_key($this->cacheByIdOld, $this->cacheById); - } - - /** - * Returns some statistics about the HierarchyCache - * - * @access public - * @return string - */ - public function GetStat() { - return sprintf("HierarchyCache is %s - Cached objects: %d", ((isset($this->cacheById))?"up":"down"), ((isset($this->cacheById))?count($this->cacheById):"0")); - } - - /** - * Returns objects which should be persistent - * called before serialization - * - * @access public - * @return array - */ - public function __sleep() { - return array("cacheById"); - } - -} diff --git a/sources/lib/core/statemanager.php b/sources/lib/core/statemanager.php deleted file mode 100644 index 0b7e7ed..0000000 --- a/sources/lib/core/statemanager.php +++ /dev/null @@ -1,539 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StateManager { - const FIXEDHIERARCHYCOUNTER = 99999; - - // backend storage types - const BACKENDSTORAGE_PERMANENT = 1; - const BACKENDSTORAGE_STATE = 2; - - private $statemachine; - private $device; - private $hierarchyOperation = false; - private $deleteOldStates = false; - - private $foldertype; - private $uuid; - private $oldStateCounter; - private $newStateCounter; - private $synchedFolders; - - - /** - * Constructor - * - * @access public - */ - public function StateManager() { - $this->statemachine = ZPush::GetStateMachine(); - $this->hierarchyOperation = ZPush::HierarchyCommand(Request::GetCommandCode()); - $this->deleteOldStates = (Request::GetCommandCode() === ZPush::COMMAND_SYNC || $this->hierarchyOperation); - $this->synchedFolders = array(); - } - - /** - * Prevents the StateMachine from removing old states - * - * @access public - * @return void - */ - public function DoNotDeleteOldStates() { - $this->deleteOldStates = false; - } - - /** - * Sets an ASDevice for the Statemanager to work with - * - * @param ASDevice $device - * - * @access public - * @return boolean - */ - public function SetDevice(&$device) { - $this->device = $device; - return true; - } - - /** - * Returns an array will all synchronized folderids - * - * @access public - * @return array - */ - public function GetSynchedFolders() { - $synched = array(); - foreach ($this->device->GetAllFolderIds() as $folderid) { - $uuid = $this->device->GetFolderUUID($folderid); - if ($uuid) - $synched[] = $folderid; - } - return $synched; - } - - /** - * Returns a folder state (SyncParameters) for a folder id - * - * @param $folderid - * - * @access public - * @return SyncParameters - */ - public function GetSynchedFolderState($folderid) { - // new SyncParameters are cached - if (isset($this->synchedFolders[$folderid])) - return $this->synchedFolders[$folderid]; - - $uuid = $this->device->GetFolderUUID($folderid); - if ($uuid) { - try { - $data = $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::FOLDERDATA, $uuid); - if ($data !== false) { - $this->synchedFolders[$folderid] = $data; - } - } - catch (StateNotFoundException $ex) { } - } - - if (!isset($this->synchedFolders[$folderid])) - $this->synchedFolders[$folderid] = new SyncParameters(); - - return $this->synchedFolders[$folderid]; - } - - /** - * Saves a folder state - SyncParameters object - * - * @param SyncParamerters $spa - * - * @access public - * @return boolean - */ - public function SetSynchedFolderState($spa) { - // make sure the current uuid is linked on the device for the folder. - // if not, old states will be automatically removed and the new ones linked - self::LinkState($this->device, $spa->GetUuid(), $spa->GetFolderId()); - - $spa->SetReferencePolicyKey($this->device->GetPolicyKey()); - - return $this->statemachine->SetState($spa, $this->device->GetDeviceId(), IStateMachine::FOLDERDATA, $spa->GetUuid()); - } - - /** - * Gets the new sync key for a specified sync key. The new sync state must be - * associated to this sync key when calling SetSyncState() - * - * @param string $synckey - * - * @access public - * @return string - */ - function GetNewSyncKey($synckey) { - if(!isset($synckey) || $synckey == "0" || $synckey == false) { - $this->uuid = $this->getNewUuid(); - $this->newStateCounter = 1; - } - else { - list($uuid, $counter) = self::ParseStateKey($synckey); - $this->uuid = $uuid; - $this->newStateCounter = $counter + 1; - } - - return self::BuildStateKey($this->uuid, $this->newStateCounter); - } - - /** - * Gets the state for a specified synckey (uuid + counter) - * - * @param string $synckey - * - * @access public - * @return string - * @throws StateInvalidException, StateNotFoundException - */ - public function GetSyncState($synckey) { - // No sync state for sync key '0' - if($synckey == "0") { - $this->oldStateCounter = 0; - return ""; - } - - // Check if synckey is allowed and set uuid and counter - list($this->uuid, $this->oldStateCounter) = self::ParseStateKey($synckey); - - // make sure the hierarchy cache is in place - if ($this->hierarchyOperation) - $this->loadHierarchyCache(); - - // the state machine will discard any sync states before this one, as they are no longer required - return $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::DEFTYPE, $this->uuid, $this->oldStateCounter, $this->deleteOldStates); - } - - /** - * Writes the sync state to a new synckey - * - * @param string $synckey - * @param string $syncstate - * @param string $folderid (opt) the synckey is associated with the folder - should always be set when performing CONTENT operations - * - * @access public - * @return boolean - * @throws StateInvalidException - */ - public function SetSyncState($synckey, $syncstate, $folderid = false) { - $internalkey = self::BuildStateKey($this->uuid, $this->newStateCounter); - if ($this->oldStateCounter != 0 && $synckey != $internalkey) - throw new StateInvalidException(sprintf("Unexpected synckey value oldcounter: '%s' synckey: '%s' internal key: '%s'", $this->oldStateCounter, $synckey, $internalkey)); - - // make sure the hierarchy cache is also saved - if ($this->hierarchyOperation) - $this->saveHierarchyCache(); - - // announce this uuid to the device, while old uuid/states should be deleted - self::LinkState($this->device, $this->uuid, $folderid); - - return $this->statemachine->SetState($syncstate, $this->device->GetDeviceId(), IStateMachine::DEFTYPE, $this->uuid, $this->newStateCounter); - } - - /** - * Gets the failsave sync state for the current synckey - * - * @access public - * @return array/boolean false if not available - */ - public function GetSyncFailState() { - if (!$this->uuid) - return false; - - try { - return $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::FAILSAVE, $this->uuid, $this->oldStateCounter, $this->deleteOldStates); - } - catch (StateNotFoundException $snfex) { - return false; - } - } - - /** - * Writes the failsave sync state for the current (old) synckey - * - * @param mixed $syncstate - * - * @access public - * @return boolean - */ - public function SetSyncFailState($syncstate) { - if ($this->oldStateCounter == 0) - return false; - - return $this->statemachine->SetState($syncstate, $this->device->GetDeviceId(), IStateMachine::FAILSAVE, $this->uuid, $this->oldStateCounter); - } - - /** - * Gets the backendstorage data - * - * @param int $type permanent or state related storage - * - * @access public - * @return mixed - * @throws StateNotYetAvailableException, StateNotFoundException - */ - public function GetBackendStorage($type = self::BACKENDSTORAGE_PERMANENT) { - if ($type == self::BACKENDSTORAGE_STATE) { - if (!$this->uuid) - throw new StateNotYetAvailableException(); - - return $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, $this->uuid, $this->oldStateCounter, $this->deleteOldStates); - } - else { - return $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, false, $this->device->GetFirstSyncTime()); - } - } - - /** - * Writes the backendstorage data - * - * @param mixed $data - * @param int $type permanent or state related storage - * - * @access public - * @return int amount of bytes saved - * @throws StateNotYetAvailableException, StateNotFoundException - */ - public function SetBackendStorage($data, $type = self::BACKENDSTORAGE_PERMANENT) { - if ($type == self::BACKENDSTORAGE_STATE) { - if (!$this->uuid) - throw new StateNotYetAvailableException(); - - // TODO serialization should be done in the StateMachine - return $this->statemachine->SetState($data, $this->device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, $this->uuid, $this->newStateCounter); - } - else { - return $this->statemachine->SetState($data, $this->device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, false, $this->device->GetFirstSyncTime()); - } - } - - /** - * Initializes the HierarchyCache for legacy syncs - * this is for AS 1.0 compatibility: - * save folder information synched with GetHierarchy() - * handled by StateManager - * - * @param string $folders Array with folder information - * - * @access public - * @return boolean - */ - public function InitializeFolderCache($folders) { - if (!is_array($folders)) - return false; - - if (!isset($this->device)) - throw new FatalException("ASDevice not initialized"); - - // redeclare this operation as hierarchyOperation - $this->hierarchyOperation = true; - - // as there is no hierarchy uuid, we have to create one - $this->uuid = $this->getNewUuid(); - $this->newStateCounter = self::FIXEDHIERARCHYCOUNTER; - - // initialize legacy HierarchCache - $this->device->SetHierarchyCache($folders); - - // force saving the hierarchy cache! - return $this->saveHierarchyCache(true); - } - - - /**---------------------------------------------------------------------------------------------------------- - * static StateManager methods - */ - - /** - * Links a folderid to the a UUID - * Old states are removed if an folderid is linked to a new UUID - * assisting the StateMachine to get rid of old data. - * - * @param ASDevice $device - * @param string $uuid the uuid to link to - * @param string $folderid (opt) if not set, hierarchy state is linked - * - * @access public - * @return boolean - */ - static public function LinkState(&$device, $newUuid, $folderid = false) { - $savedUuid = $device->GetFolderUUID($folderid); - // delete 'old' states! - if ($savedUuid != $newUuid) { - // remove states but no need to notify device - self::UnLinkState($device, $folderid, false); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("StateManager::linkState(#ASDevice, '%s','%s'): linked to uuid '%s'.", $newUuid, (($folderid === false)?'HierarchyCache':$folderid), $newUuid)); - return $device->SetFolderUUID($newUuid, $folderid); - } - return true; - } - - /** - * UnLinks all states from a folder id - * Old states are removed assisting the StateMachine to get rid of old data. - * The UUID is then removed from the device - * - * @param ASDevice $device - * @param string $folderid - * @param boolean $removeFromDevice indicates if the device should be - * notified that the state was removed - * @param boolean $retrieveUUIDFromDevice indicates if the UUID should be retrieved from - * device. If not true this parameter will be used as UUID. - * - * @access public - * @return boolean - */ - static public function UnLinkState(&$device, $folderid, $removeFromDevice = true, $retrieveUUIDFromDevice = true) { - if ($retrieveUUIDFromDevice === true) - $savedUuid = $device->GetFolderUUID($folderid); - else - $savedUuid = $retrieveUUIDFromDevice; - - if ($savedUuid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("StateManager::UnLinkState('%s'): saved state '%s' will be deleted.", $folderid, $savedUuid)); - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::DEFTYPE, $savedUuid, self::FIXEDHIERARCHYCOUNTER *2); - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::FOLDERDATA, $savedUuid); // CPO - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::FAILSAVE, $savedUuid, self::FIXEDHIERARCHYCOUNTER *2); - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, $savedUuid, self::FIXEDHIERARCHYCOUNTER *2); - - // remove all messages which could not be synched before - $device->RemoveIgnoredMessage($folderid, false); - - if ($folderid === false && $savedUuid !== false) - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::HIERARCHY, $savedUuid, self::FIXEDHIERARCHYCOUNTER *2); - } - // delete this id from the uuid cache - if ($removeFromDevice) - return $device->SetFolderUUID(false, $folderid); - else - return true; - } - - /** - * Parses a SyncKey and returns UUID and counter - * - * @param string $synckey - * - * @access public - * @return array uuid, counter - * @throws StateInvalidException - */ - static public function ParseStateKey($synckey) { - $matches = array(); - if(!preg_match('/^\{([0-9A-Za-z-]+)\}([0-9]+)$/', $synckey, $matches)) - throw new StateInvalidException(sprintf("SyncKey '%s' is invalid", $synckey)); - - return array($matches[1], (int)$matches[2]); - } - - /** - * Builds a SyncKey from a UUID and counter - * - * @param string $uuid - * @param int $counter - * - * @access public - * @return string syncKey - * @throws StateInvalidException - */ - static public function BuildStateKey($uuid, $counter) { - if(!preg_match('/^([0-9A-Za-z-]+)$/', $uuid, $matches)) - throw new StateInvalidException(sprintf("UUID '%s' is invalid", $uuid)); - - return "{" . $uuid . "}" . $counter; - } - - - /**---------------------------------------------------------------------------------------------------------- - * private StateManager methods - */ - - /** - * Loads the HierarchyCacheState and initializes the HierarchyChache - * if this is an hierarchy operation - * - * @access private - * @return boolean - * @throws StateNotFoundException - */ - private function loadHierarchyCache() { - if (!$this->hierarchyOperation) - return false; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("StateManager->loadHierarchyCache(): '%s-%s-%s-%d'", $this->device->GetDeviceId(), $this->uuid, IStateMachine::HIERARCHY, $this->oldStateCounter)); - - // check if a full hierarchy sync might be necessary - if ($this->device->GetFolderUUID(false) === false) { - self::UnLinkState($this->device, false, false, $this->uuid); - throw new StateNotFoundException("No hierarchy UUID linked to device. Requesting folder resync."); - } - - $hierarchydata = $this->statemachine->GetState($this->device->GetDeviceId(), IStateMachine::HIERARCHY, $this->uuid , $this->oldStateCounter, $this->deleteOldStates); - $this->device->SetHierarchyCache($hierarchydata); - return true; - } - - /** - * Saves the HierarchyCacheState of the HierarchyChache - * if this is an hierarchy operation - * - * @param boolean $forceLoad indicates if the cache should be saved also if not a hierary operation - * - * @access private - * @return boolean - * @throws StateInvalidException - */ - private function saveHierarchyCache($forceSaving = false) { - if (!$this->hierarchyOperation && !$forceSaving) - return false; - - // link the hierarchy cache again, if the UUID does not match the UUID saved in the devicedata - if (($this->uuid != $this->device->GetFolderUUID() || $forceSaving) ) - self::LinkState($this->device, $this->uuid); - - // check all folders and deleted folders to update data of ASDevice and delete old states - $hc = $this->device->getHierarchyCache(); - foreach ($hc->GetDeletedFolders() as $delfolder) - self::UnLinkState($this->device, $delfolder->serverid); - - foreach ($hc->ExportFolders() as $folder) - $this->device->SetFolderType($folder->serverid, $folder->type); - - return $this->statemachine->SetState($this->device->GetHierarchyCacheData(), $this->device->GetDeviceId(), IStateMachine::HIERARCHY, $this->uuid, $this->newStateCounter); - } - - /** - * Generates a new UUID - * - * @access private - * @return string - */ - private function getNewUuid() { - return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', - mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), - mt_rand( 0, 0x0fff ) | 0x4000, - mt_rand( 0, 0x3fff ) | 0x8000, - mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) ); - } -} diff --git a/sources/lib/core/stateobject.php b/sources/lib/core/stateobject.php deleted file mode 100644 index c4542f6..0000000 --- a/sources/lib/core/stateobject.php +++ /dev/null @@ -1,266 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StateObject implements Serializable { - private $SO_internalid; - protected $data = array(); - protected $unsetdata = array(); - protected $changed = false; - - /** - * Returns the unique id of that data object - * - * @access public - * @return array - */ - public function GetID() { - if (!isset($this->SO_internalid)) - $this->SO_internalid = sprintf('%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); - - return $this->SO_internalid; - } - - /** - * Returns the internal array which contains all data of this object - * - * @access public - * @return array - */ - public function GetDataArray() { - return $this->data; - } - - /** - * Sets the internal array which contains all data of this object - * - * @param array $data the data to be written - * @param boolean $markAsChanged (opt) indicates if the object should be marked as "changed", default false - * - * @access public - * @return array - */ - public function SetDataArray($data, $markAsChanged = false) { - $this->data = $data; - $this->changed = $markAsChanged; - } - - /** - * Indicates if the data contained in this object was modified - * - * @access public - * @return array - */ - public function IsDataChanged() { - return $this->changed; - } - - /** - * PHP magic to set an instance variable - * - * @access public - * @return - */ - public function __set($name, $value) { - $lname = strtolower($name); - if (isset($this->data[$lname]) && is_scalar($value) && !is_array($value) && $this->data[$lname] === $value) - return false; - - $this->data[$lname] = $value; - $this->changed = true; - } - - /** - * PHP magic to get an instance variable - * if the variable was not set previousely, the value of the - * Unsetdata array is returned - * - * @access public - * @return - */ - public function __get($name) { - $lname = strtolower($name); - - if (array_key_exists($lname, $this->data)) - return $this->data[$lname]; - - if (isset($this->unsetdata) && is_array($this->unsetdata) && array_key_exists($lname, $this->unsetdata)) - return $this->unsetdata[$lname]; - - return null; - } - - /** - * PHP magic to check if an instance variable is set - * - * @access public - * @return - */ - public function __isset($name) { - return isset($this->data[strtolower($name)]); - } - - /** - * PHP magic to remove an instance variable - * - * @access public - * @return - */ - public function __unset($name) { - if (isset($this->$name)) { - unset($this->data[strtolower($name)]); - $this->changed = true; - } - } - - /** - * PHP magic to implement any getter, setter, has and delete operations - * on an instance variable. - * Methods like e.g. "SetVariableName($x)" and "GetVariableName()" are supported - * - * @access public - * @return mixed - */ - public function __call($name, $arguments) { - $name = strtolower($name); - $operator = substr($name, 0,3); - $var = substr($name,3); - - if ($operator == "set" && count($arguments) == 1){ - $this->$var = $arguments[0]; - return true; - } - - if ($operator == "set" && count($arguments) == 2 && $arguments[1] === false){ - $this->data[$var] = $arguments[0]; - return true; - } - - // getter without argument = return variable, null if not set - if ($operator == "get" && count($arguments) == 0) { - return $this->$var; - } - - // getter with one argument = return variable if set, else the argument - else if ($operator == "get" && count($arguments) == 1) { - if (isset($this->$var)) { - return $this->$var; - } - else - return $arguments[0]; - } - - if ($operator == "has" && count($arguments) == 0) - return isset($this->$var); - - if ($operator == "del" && count($arguments) == 0) { - unset($this->$var); - return true; - } - - throw new FatalNotImplementedException(sprintf("StateObject->__call('%s'): not implemented. op: {%s} args: %d", $name, $operator, count($arguments))); - } - - /** - * Method to serialize a StateObject - * - * @access public - * @return array - */ - public function serialize() { - // perform tasks just before serialization - $this->preSerialize(); - - return serialize(array($this->SO_internalid,$this->data)); - } - - /** - * Method to unserialize a StateObject - * - * @access public - * @return array - * @throws StateInvalidException - */ - public function unserialize($data) { - // throw a StateInvalidException if unserialize fails - ini_set('unserialize_callback_func', 'StateObject::ThrowStateInvalidException'); - - list($this->SO_internalid, $this->data) = unserialize($data); - - // perform tasks just after unserialization - $this->postUnserialize(); - return true; - } - - /** - * Called before the StateObject is serialized - * - * @access protected - * @return boolean - */ - protected function preSerialize() { - // make sure the object has an id before serialization - $this->GetID(); - - return true; - } - - /** - * Called after the StateObject was unserialized - * - * @access protected - * @return boolean - */ - protected function postUnserialize() { - return true; - } - - /** - * Callback function for failed unserialize - * - * @access public - * @throws StateInvalidException - */ - public static function ThrowStateInvalidException() { - throw new StateInvalidException("Unserialization failed as class was not found or not compatible"); - } -} diff --git a/sources/lib/core/streamer.php b/sources/lib/core/streamer.php deleted file mode 100644 index d7df277..0000000 --- a/sources/lib/core/streamer.php +++ /dev/null @@ -1,451 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Streamer implements Serializable { - const STREAMER_VAR = 1; - const STREAMER_ARRAY = 2; - const STREAMER_TYPE = 3; - const STREAMER_PROP = 4; - const STREAMER_TYPE_DATE = 1; - const STREAMER_TYPE_HEX = 2; - const STREAMER_TYPE_DATE_DASHES = 3; - const STREAMER_TYPE_STREAM = 4; - const STREAMER_TYPE_IGNORE = 5; - const STREAMER_TYPE_SEND_EMPTY = 6; - const STREAMER_TYPE_NO_CONTAINER = 7; - const STREAMER_TYPE_COMMA_SEPARATED = 8; - const STREAMER_TYPE_SEMICOLON_SEPARATED = 9; - const STREAMER_TYPE_MULTIPART = 10; - - protected $mapping; - public $flags; - public $content; - - /** - * Constructor - * - * @param array $mapping internal mapping of variables - * @access public - */ - function Streamer($mapping) { - $this->mapping = $mapping; - $this->flags = false; - } - - /** - * Decodes the WBXML from a WBXMLdecoder until we reach the same depth level of WBXML. - * This means that if there are multiple objects at this level, then only the first is - * decoded SubOjects are auto-instantiated and decoded using the same functionality - * - * @param WBXMLDecoder $decoder - * - * @access public - */ - public function Decode(&$decoder) { - while(1) { - $entity = $decoder->getElement(); - - if($entity[EN_TYPE] == EN_TYPE_STARTTAG) { - if(! ($entity[EN_FLAGS] & EN_FLAGS_CONTENT)) { - $map = $this->mapping[$entity[EN_TAG]]; - if (isset($map[self::STREAMER_ARRAY])) { - $this->$map[self::STREAMER_VAR] = array(); - } else if(!isset($map[self::STREAMER_TYPE])) { - $this->$map[self::STREAMER_VAR] = ""; - } - else if ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES ) { - $this->$map[self::STREAMER_VAR] = ""; - } - else if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) - $this->$map[self::STREAMER_VAR] = ""; - continue; - } - // Found a start tag - if(!isset($this->mapping[$entity[EN_TAG]])) { - // This tag shouldn't be here, abort - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Tag '%s' unexpected in type XML type '%s'", $entity[EN_TAG], get_class($this))); - return false; - } - else { - $map = $this->mapping[$entity[EN_TAG]]; - - // Handle an array - if(isset($map[self::STREAMER_ARRAY])) { - while(1) { - //do not get start tag for an array without a container - if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) { - if(!$decoder->getElementStartTag($map[self::STREAMER_ARRAY])) - break; - } - if(isset($map[self::STREAMER_TYPE])) { - $decoded = new $map[self::STREAMER_TYPE]; - - $decoded->Decode($decoder); - } - else { - $decoded = $decoder->getElementContent(); - } - - if(!isset($this->$map[self::STREAMER_VAR])) - $this->$map[self::STREAMER_VAR] = array($decoded); - else - array_push($this->$map[self::STREAMER_VAR], $decoded); - - if(!$decoder->getElementEndTag()) //end tag of a container element - return false; - - if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER) { - $e = $decoder->peek(); - //go back to the initial while if another block of no container elements is found - if ($e[EN_TYPE] == EN_TYPE_STARTTAG) { - continue 2; - } - //break on end tag because no container elements block end is reached - if ($e[EN_TYPE] == EN_TYPE_ENDTAG) - break; - if (empty($e)) - break; - } - } - //do not get end tag for an array without a container - if (!(isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_NO_CONTAINER)) { - if(!$decoder->getElementEndTag()) //end tag of container - return false; - } - } - else { // Handle single value - if(isset($map[self::STREAMER_TYPE])) { - // Complex type, decode recursively - if($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES) { - $decoded = $this->parseDate($decoder->getElementContent()); - if(!$decoder->getElementEndTag()) - return false; - } - else if($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) { - $decoded = hex2bin($decoder->getElementContent()); - if(!$decoder->getElementEndTag()) - return false; - } - // explode comma or semicolon strings into arrays - else if($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED) { - $glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED)?", ":"; "; - $decoded = explode($glue, $decoder->getElementContent()); - if(!$decoder->getElementEndTag()) - return false; - } - else { - $subdecoder = new $map[self::STREAMER_TYPE](); - if($subdecoder->Decode($decoder) === false) - return false; - - $decoded = $subdecoder; - - if(!$decoder->getElementEndTag()) { - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("No end tag for '%s'", $entity[EN_TAG])); - return false; - } - } - } - else { - // Simple type, just get content - $decoded = $decoder->getElementContent(); - - if($decoded === false) { - // the tag is declared to have content, but no content is available. - // set an empty content - $decoded = ""; - } - - if(!$decoder->getElementEndTag()) { - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("Unable to get end tag for '%s'", $entity[EN_TAG])); - return false; - } - } - // $decoded now contains data object (or string) - $this->$map[self::STREAMER_VAR] = $decoded; - } - } - } - else if($entity[EN_TYPE] == EN_TYPE_ENDTAG) { - $decoder->ungetElement($entity); - break; - } - else { - ZLog::Write(LOGLEVEL_WBXMLSTACK, "Unexpected content in type"); - break; - } - } - } - - /** - * Encodes this object and any subobjects - output is ordered according to mapping - * - * @param WBXMLEncoder $encoder - * - * @access public - */ - public function Encode(&$encoder) { - // A return value if anything was streamed. We need for empty tags. - $streamed = false; - foreach($this->mapping as $tag => $map) { - if(isset($this->$map[self::STREAMER_VAR])) { - // Variable is available - if(is_object($this->$map[self::STREAMER_VAR])) { - // Subobjects can do their own encoding - if ($this->$map[self::STREAMER_VAR] instanceof Streamer) { - $encoder->startTag($tag); - $res = $this->$map[self::STREAMER_VAR]->Encode($encoder); - $encoder->endTag(); - // nothing was streamed in previous encode but it should be streamed empty anyway - if (!$res && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) - $encoder->startTag($tag, false, true); - } - else - ZLog::Write(LOGLEVEL_ERROR, sprintf("Streamer->Encode(): parameter '%s' of object %s is not of type Streamer", $map[self::STREAMER_VAR], get_class($this))); - } - // Array of objects - else if(isset($map[self::STREAMER_ARRAY])) { - if (empty($this->$map[self::STREAMER_VAR]) && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) { - $encoder->startTag($tag, false, true); - } - else { - // Outputs array container (eg Attachments) - // Do not output start and end tag when type is STREAMER_TYPE_NO_CONTAINER - if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) - $encoder->startTag($tag); - - foreach ($this->$map[self::STREAMER_VAR] as $element) { - if(is_object($element)) { - $encoder->startTag($map[self::STREAMER_ARRAY]); // Outputs object container (eg Attachment) - $element->Encode($encoder); - $encoder->endTag(); - } - else { - if(strlen($element) == 0) - // Do not output empty items. Not sure if we should output an empty tag with $encoder->startTag($map[self::STREAMER_ARRAY], false, true); - ; - else { - $encoder->startTag($map[self::STREAMER_ARRAY]); - $encoder->content($element); - $encoder->endTag(); - $streamed = true; - } - } - } - - if (!isset($map[self::STREAMER_PROP]) || $map[self::STREAMER_PROP] != self::STREAMER_TYPE_NO_CONTAINER) - $encoder->endTag(); - } - } - else { - if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_IGNORE) { - continue; - } - - if ($encoder->getMultipart() && isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_MULTIPART) { - $encoder->addBodypartStream($this->$map[self::STREAMER_VAR]); - $encoder->startTag(SYNC_ITEMOPERATIONS_PART); - $encoder->content($encoder->getBodypartsCount()); - $encoder->endTag(); - continue; - } - - // Simple type - if(!isset($map[self::STREAMER_TYPE]) && strlen($this->$map[self::STREAMER_VAR]) == 0) { - // send empty tags - if (isset($map[self::STREAMER_PROP]) && $map[self::STREAMER_PROP] == self::STREAMER_TYPE_SEND_EMPTY) - $encoder->startTag($tag, false, true); - - // Do not output empty items. See above: $encoder->startTag($tag, false, true); - continue; - } else - $encoder->startTag($tag); - - if(isset($map[self::STREAMER_TYPE]) && ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_DATE_DASHES)) { - if($this->$map[self::STREAMER_VAR] != 0) // don't output 1-1-1970 - $encoder->content($this->formatDate($this->$map[self::STREAMER_VAR], $map[self::STREAMER_TYPE])); - } - else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_HEX) { - $encoder->content(strtoupper(bin2hex($this->$map[self::STREAMER_VAR]))); - } - else if(isset($map[self::STREAMER_TYPE]) && $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_STREAM) { - //we need to encode in base64 - $encoder->content(Utils::EncodeBase64($this->$map[self::STREAMER_VAR])); - //memory ... - $this->$map[self::STREAMER_VAR] = null; - } - // implode comma or semicolon arrays into a string - else if(isset($map[self::STREAMER_TYPE]) && is_array($this->$map[self::STREAMER_VAR]) && - ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED || $map[self::STREAMER_TYPE] == self::STREAMER_TYPE_SEMICOLON_SEPARATED)) { - $glue = ($map[self::STREAMER_TYPE] == self::STREAMER_TYPE_COMMA_SEPARATED)?", ":"; "; - $encoder->content(implode($glue, $this->$map[self::STREAMER_VAR])); - } - else { - $encoder->content($this->$map[self::STREAMER_VAR]); - } - $encoder->endTag(); - $streamed = true; - } - } - } - // Output our own content - if(isset($this->content)) - $encoder->content($this->content); - - return $streamed; - } - - /** - * Removes not necessary data from the object - * - * @access public - * @return boolean - */ - public function StripData() { - foreach ($this->mapping as $k=>$v) { - if (isset($this->$v[self::STREAMER_VAR])) { - if (is_object($this->$v[self::STREAMER_VAR]) && method_exists($this->$v[self::STREAMER_VAR], "StripData") ) { - $this->$v[self::STREAMER_VAR]->StripData(); - } - else if (isset($v[self::STREAMER_ARRAY]) && !empty($this->$v[self::STREAMER_VAR])) { - foreach ($this->$v[self::STREAMER_VAR] as $element) { - if (is_object($element) && method_exists($element, "StripData") ) { - $element->StripData(); - } - } - } - } - } - unset($this->mapping); - - return true; - } - - /** - * Method to serialize a Streamer and respective SyncObject - * - * @access public - * @return array - */ - public function serialize() { - $values = array(); - foreach ($this->mapping as $k=>$v) { - if (isset($this->$v[self::STREAMER_VAR])) - $values[$v[self::STREAMER_VAR]] = serialize($this->$v[self::STREAMER_VAR]); - } - - return serialize($values); - } - - /** - * Method to unserialize a Streamer and respective SyncObject - * - * @access public - * @return array - */ - public function unserialize($data) { - $class = get_class($this); - $this->$class(); - $values = unserialize($data); - foreach ($values as $k=>$v) - $this->$k = unserialize($v); - - return true; - } - - /**---------------------------------------------------------------------------------------------------------- - * Private methods for conversion - */ - - /** - * Formats a timestamp - * Oh yeah, this is beautiful. Exchange outputs date fields differently in calendar items - * and emails. We could just always send one or the other, but unfortunately nokia's 'Mail for - * exchange' depends on this quirk. So we have to send a different date type depending on where - * it's used. Sigh. - * - * @param long $ts - * @param int $type - * - * @access private - * @return string - */ - private function formatDate($ts, $type) { - if($type == self::STREAMER_TYPE_DATE) - return gmstrftime("%Y%m%dT%H%M%SZ", $ts); - else if($type == self::STREAMER_TYPE_DATE_DASHES) - return gmstrftime("%Y-%m-%dT%H:%M:%S.000Z", $ts); - } - - /** - * Transforms an AS timestamp into a unix timestamp - * - * @param string $ts - * - * @access private - * @return long - */ - function parseDate($ts) { - if(preg_match("/(\d{4})[^0-9]*(\d{2})[^0-9]*(\d{2})(T(\d{2})[^0-9]*(\d{2})[^0-9]*(\d{2})(.\d+)?Z){0,1}$/", $ts, $matches)) { - if ($matches[1] >= 2038){ - $matches[1] = 2038; - $matches[2] = 1; - $matches[3] = 18; - $matches[5] = $matches[6] = $matches[7] = 0; - } - - if (!isset($matches[5])) $matches[5] = 0; - if (!isset($matches[6])) $matches[6] = 0; - if (!isset($matches[7])) $matches[7] = 0; - - return gmmktime($matches[5], $matches[6], $matches[7], $matches[2], $matches[3], $matches[1]); - } - return 0; - } -} diff --git a/sources/lib/core/streamimporter.php b/sources/lib/core/streamimporter.php deleted file mode 100644 index 0db17f5..0000000 --- a/sources/lib/core/streamimporter.php +++ /dev/null @@ -1,260 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ImportChangesStream implements IImportChanges { - private $encoder; - private $objclass; - private $seenObjects; - private $importedMsgs; - private $checkForIgnoredMessages; - - /** - * Constructor of the StreamImporter - * - * @param WBXMLEncoder $encoder Objects are streamed to this encoder - * @param SyncObject $class SyncObject class (only these are accepted when streaming content messages) - * - * @access public - */ - public function ImportChangesStream(&$encoder, $class) { - $this->encoder = &$encoder; - $this->objclass = $class; - $this->classAsString = (is_object($class))?get_class($class):''; - $this->seenObjects = array(); - $this->importedMsgs = 0; - $this->checkForIgnoredMessages = true; - } - - /** - * Implement interface - never used - */ - public function Config($state, $flags = 0) { return true; } - public function ConfigContentParameters($contentparameters) { return true; } - public function GetState() { return false;} - public function LoadConflicts($contentparameters, $state) { return true; } - - /** - * Imports a single message - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean - */ - public function ImportMessageChange($id, $message) { - // ignore other SyncObjects - if(!($message instanceof $this->classAsString)) - return false; - - // prevent sending the same object twice in one request - if (in_array($id, $this->seenObjects)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Object '%s' discarded! Object already sent in this request.", $id)); - return true; - } - - $this->importedMsgs++; - $this->seenObjects[] = $id; - - // checks if the next message may cause a loop or is broken - if (ZPush::GetDeviceManager()->DoNotStreamMessage($id, $message)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): message ignored and requested to be removed from mobile", $id)); - - // this is an internal operation & should not trigger an update in the device manager - $this->checkForIgnoredMessages = false; - $stat = $this->ImportMessageDeletion($id); - $this->checkForIgnoredMessages = true; - - return $stat; - } - - if ($message->flags === false || $message->flags === SYNC_NEWMESSAGE) - $this->encoder->startTag(SYNC_ADD); - else { - // on update of an SyncEmail we only export the flags - if($message instanceof SyncMail && isset($message->flag) && $message->flag instanceof SyncMailFlags) { - $newmessage = new SyncMail(); - $newmessage->read = $message->read; - $newmessage->flag = $message->flag; - if (isset($message->lastverbexectime)) $newmessage->lastverbexectime = $message->lastverbexectime; - if (isset($message->lastverbexecuted)) $newmessage->lastverbexecuted = $message->lastverbexecuted; - $message = $newmessage; - unset($newmessage); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportMessageChange('%s'): SyncMail message updated. Message content is striped, only flags are streamed.", $id)); - } - - $this->encoder->startTag(SYNC_MODIFY); - } - $this->encoder->startTag(SYNC_SERVERENTRYID); - $this->encoder->content($id); - $this->encoder->endTag(); - $this->encoder->startTag(SYNC_DATA); - $message->Encode($this->encoder); - $this->encoder->endTag(); - $this->encoder->endTag(); - - return true; - } - - /** - * Imports a deletion - * - * @param string $id - * - * @access public - * @return boolean - */ - public function ImportMessageDeletion($id) { - if ($this->checkForIgnoredMessages) { - ZPush::GetDeviceManager()->RemoveBrokenMessage($id); - } - - $this->importedMsgs++; - $this->encoder->startTag(SYNC_REMOVE); - $this->encoder->startTag(SYNC_SERVERENTRYID); - $this->encoder->content($id); - $this->encoder->endTag(); - $this->encoder->endTag(); - - return true; - } - - /** - * Imports a change in 'read' flag - * Can only be applied to SyncMail (Email) requests - * - * @param string $id - * @param int $flags - read/unread - * - * @access public - * @return boolean - */ - public function ImportMessageReadFlag($id, $flags) { - if(!($this->objclass instanceof SyncMail)) - return false; - - $this->importedMsgs++; - - $this->encoder->startTag(SYNC_MODIFY); - $this->encoder->startTag(SYNC_SERVERENTRYID); - $this->encoder->content($id); - $this->encoder->endTag(); - $this->encoder->startTag(SYNC_DATA); - $this->encoder->startTag(SYNC_POOMMAIL_READ); - $this->encoder->content($flags); - $this->encoder->endTag(); - $this->encoder->endTag(); - $this->encoder->endTag(); - - return true; - } - - /** - * ImportMessageMove is not implemented, as this operation can not be streamed to a WBXMLEncoder - * - * @param string $id - * @param int $flags read/unread - * - * @access public - * @return boolean - */ - public function ImportMessageMove($id, $newfolder) { - return true; - } - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return string id of the folder - */ - public function ImportFolderChange($folder) { - // checks if the next message may cause a loop or is broken - if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($folder->serverid, $folder)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ImportChangesStream->ImportFolderChange('%s'): folder ignored as requested by DeviceManager.", $folder->serverid)); - return true; - } - - // send a modify flag if the folder is already known on the device - if (isset($folder->flags) && $folder->flags === SYNC_NEWMESSAGE) - $this->encoder->startTag(SYNC_FOLDERHIERARCHY_ADD); - else - $this->encoder->startTag(SYNC_FOLDERHIERARCHY_UPDATE); - - $folder->Encode($this->encoder); - $this->encoder->endTag(); - - return true; - } - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent id - * - * @access public - * @return boolean - */ - public function ImportFolderDeletion($id, $parent = false) { - $this->encoder->startTag(SYNC_FOLDERHIERARCHY_REMOVE); - $this->encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID); - $this->encoder->content($id); - $this->encoder->endTag(); - $this->encoder->endTag(); - - return true; - } - - /** - * Returns the number of messages which were changed, deleted and had changed read status - * - * @access public - * @return int - */ - public function GetImportedMessages() { - return $this->importedMsgs; - } -} diff --git a/sources/lib/core/synccollections.php b/sources/lib/core/synccollections.php deleted file mode 100644 index e3cf545..0000000 --- a/sources/lib/core/synccollections.php +++ /dev/null @@ -1,724 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncCollections implements Iterator { - const ERROR_NO_COLLECTIONS = 1; - const ERROR_WRONG_HIERARCHY = 2; - const OBSOLETE_CONNECTION = 3; - const HIERARCHY_CHANGED = 4; - - private $stateManager; - - private $collections = array(); - private $addparms = array(); - private $changes = array(); - private $saveData = true; - - private $refPolicyKey = false; - private $refLifetime = false; - - private $globalWindowSize; - private $lastSyncTime; - - private $waitingTime = 0; - - - /** - * Constructor - */ - public function SyncCollections() { - } - - /** - * Sets the StateManager for this object - * If this is not done and a method needs it, the StateManager will be - * requested from the DeviceManager - * - * @param StateManager $statemanager - * - * @access public - * @return - */ - public function SetStateManager($statemanager) { - $this->stateManager = $statemanager; - } - - /** - * Loads all collections known for the current device - * - * @param boolean $overwriteLoaded (opt) overwrites Collection with saved state if set to true - * @param boolean $loadState (opt) indicates if the collection sync state should be loaded, default true - * @param boolean $checkPermissions (opt) if set to true each folder will pass - * through a backend->Setup() to check permissions. - * If this fails a StatusException will be thrown. - * - * @access public - * @throws StatusException with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails - * @throws StateNotFoundException if the sync state can not be found ($loadState = true) - * @return boolean - */ - public function LoadAllCollections($overwriteLoaded = false, $loadState = false, $checkPermissions = false) { - $this->loadStateManager(); - - // this operation should not remove old state counters - $this->stateManager->DoNotDeleteOldStates(); - - $invalidStates = false; - foreach($this->stateManager->GetSynchedFolders() as $folderid) { - if ($overwriteLoaded === false && isset($this->collections[$folderid])) - continue; - - // Load Collection! - if (! $this->LoadCollection($folderid, $loadState, $checkPermissions)) - $invalidStates = true; - } - - if ($invalidStates) - throw new StateInvalidException("Invalid states found while loading collections. Forcing sync"); - - return true; - } - - /** - * Loads all collections known for the current device - * - * @param string $folderid folder id to be loaded - * @param boolean $loadState (opt) indicates if the collection sync state should be loaded, default true - * @param boolean $checkPermissions (opt) if set to true each folder will pass - * through a backend->Setup() to check permissions. - * If this fails a StatusException will be thrown. - * - * @access public - * @throws StatusException with SyncCollections::ERROR_WRONG_HIERARCHY if permission check fails - * @throws StateNotFoundException if the sync state can not be found ($loadState = true) - * @return boolean - */ - public function LoadCollection($folderid, $loadState = false, $checkPermissions = false) { - $this->loadStateManager(); - - try { - // Get SyncParameters for the folder from the state - $spa = $this->stateManager->GetSynchedFolderState($folderid); - - // TODO remove resync of folders for < Z-Push 2 beta4 users - // this forces a resync of all states previous to Z-Push 2 beta4 - if (! $spa instanceof SyncParameters) - throw new StateInvalidException("Saved state are not of type SyncParameters"); - } - catch (StateInvalidException $sive) { - // in case there is something wrong with the state, just stop here - // later when trying to retrieve the SyncParameters nothing will be found - - // we also generate a fake change, so a sync on this folder is triggered - $this->changes[$folderid] = 1; - - return false; - } - - // if this is an additional folder the backend has to be setup correctly - if ($checkPermissions === true && ! ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) - throw new StatusException(sprintf("SyncCollections->LoadCollection(): could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), self::ERROR_WRONG_HIERARCHY); - - // add collection to object - $addStatus = $this->AddCollection($spa); - - // load the latest known syncstate if requested - if ($addStatus && $loadState === true) - $this->addparms[$folderid]["state"] = $this->stateManager->GetSyncState($spa->GetLatestSyncKey()); - - return $addStatus; - } - - /** - * Saves a SyncParameters Object - * - * @param SyncParamerts $spa - * - * @access public - * @return boolean - */ - public function SaveCollection($spa) { - if (! $this->saveData || !$spa->HasFolderId()) - return false; - - if ($spa->IsDataChanged()) { - $this->loadStateManager(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->SaveCollection(): Data of folder '%s' changed", $spa->GetFolderId())); - - // save new windowsize - if (isset($this->globalWindowSize)) - $spa->SetWindowSize($this->globalWindowSize); - - // update latest lifetime - if (isset($this->refLifetime)) - $spa->SetReferenceLifetime($this->refLifetime); - - return $this->stateManager->SetSynchedFolderState($spa); - } - return false; - } - - /** - * Adds a SyncParameters object to the current list of collections - * - * @param SyncParameters $spa - * - * @access public - * @return boolean - */ - public function AddCollection($spa) { - if (! $spa->HasFolderId()) - return false; - - $this->collections[$spa->GetFolderId()] = $spa; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Folder id '%s' : ref. PolicyKey '%s', ref. Lifetime '%s', last sync at '%s'", $spa->GetFolderId(), $spa->GetReferencePolicyKey(), $spa->GetReferenceLifetime(), $spa->GetLastSyncTime())); - if ($spa->HasLastSyncTime() && $spa->GetLastSyncTime() > $this->lastSyncTime) { - $this->lastSyncTime = $spa->GetLastSyncTime(); - - // use SyncParameters PolicyKey as reference if available - if ($spa->HasReferencePolicyKey()) - $this->refPolicyKey = $spa->GetReferencePolicyKey(); - - // use SyncParameters LifeTime as reference if available - if ($spa->HasReferenceLifetime()) - $this->refLifetime = $spa->GetReferenceLifetime(); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->AddCollection(): Updated reference PolicyKey '%s', reference Lifetime '%s', Last sync at '%s'", $this->refPolicyKey, $this->refLifetime, $this->lastSyncTime)); - } - - return true; - } - - /** - * Returns a previousily added or loaded SyncParameters object for a folderid - * - * @param SyncParameters $spa - * - * @access public - * @return SyncParameters / boolean false if no SyncParameters object is found for folderid - */ - public function GetCollection($folderid) { - if (isset($this->collections[$folderid])) - return $this->collections[$folderid]; - else - return false; - } - - /** - * Indicates if there are any loaded CPOs - * - * @access public - * @return boolean - */ - public function HasCollections() { - return ! empty($this->collections); - } - - /** - * Add a non-permanent key/value pair for a SyncParameters object - * - * @param SyncParameters $spa target SyncParameters - * @param string $key - * @param mixed $value - * - * @access public - * @return boolean - */ - public function AddParameter($spa, $key, $value) { - if (!$spa->HasFolderId()) - return false; - - $folderid = $spa->GetFolderId(); - if (!isset($this->addparms[$folderid])) - $this->addparms[$folderid] = array(); - - $this->addparms[$folderid][$key] = $value; - return true; - } - - /** - * Returns a previousily set non-permanent value for a SyncParameters object - * - * @param SyncParameters $spa target SyncParameters - * @param string $key - * - * @access public - * @return mixed returns 'null' if nothing set - */ - public function GetParameter($spa, $key) { - if (!$spa->HasFolderId()) - return null; - - if (isset($this->addparms[$spa->GetFolderId()]) && isset($this->addparms[$spa->GetFolderId()][$key])) - return $this->addparms[$spa->GetFolderId()][$key]; - else - return null; - } - - /** - * Returns the latest known PolicyKey to be used as reference - * - * @access public - * @return int/boolen returns false if nothing found in collections - */ - public function GetReferencePolicyKey() { - return $this->refPolicyKey; - } - - /** - * Sets a global window size which should be used for all collections - * in a case of a heartbeat and/or partial sync - * - * @param int $windowsize - * - * @access public - * @return boolean - */ - public function SetGlobalWindowSize($windowsize) { - $this->globalWindowSize = $windowsize; - return true; - } - - /** - * Returns the global window size which should be used for all collections - * in a case of a heartbeat and/or partial sync - * - * @access public - * @return int/boolean returns false if not set or not available - */ - public function GetGlobalWindowSize() { - if (!isset($this->globalWindowSize)) - return false; - - return $this->globalWindowSize; - } - - /** - * Sets the lifetime for heartbeat or ping connections - * - * @param int $lifetime time in seconds - * - * @access public - * @return boolean - */ - public function SetLifetime($lifetime) { - $this->refLifetime = $lifetime; - return true; - } - - /** - * Sets the lifetime for heartbeat or ping connections - * previousily set or saved in a collection - * - * @access public - * @return int returns 600 as default if nothing set or not available - */ - public function GetLifetime() { - if (!isset( $this->refLifetime) || $this->refLifetime === false) - return 600; - - return $this->refLifetime; - } - - /** - * Returns the timestamp of the last synchronization for all - * loaded collections - * - * @access public - * @return int timestamp - */ - public function GetLastSyncTime() { - return $this->lastSyncTime; - } - - /** - * Checks if the currently known collections for changes for $lifetime seconds. - * If the backend provides a ChangesSink the sink will be used. - * If not every $interval seconds an exporter will be configured for each - * folder to perform GetChangeCount(). - * - * @param int $lifetime (opt) total lifetime to wait for changes / default 600s - * @param int $interval (opt) time between blocking operations of sink or polling / default 30s - * @param boolean $onlyPingable (opt) only check for folders which have the PingableFlag - * - * @access public - * @return boolean indicating if changes were found - * @throws StatusException with code SyncCollections::ERROR_NO_COLLECTIONS if no collections available - * with code SyncCollections::ERROR_WRONG_HIERARCHY if there were errors getting changes - */ - public function CheckForChanges($lifetime = 600, $interval = 30, $onlyPingable = false) { - $classes = array(); - foreach ($this->collections as $folderid => $spa){ - if ($onlyPingable && $spa->GetPingableFlag() !== true) - continue; - - if (!isset($classes[$spa->GetContentClass()])) - $classes[$spa->GetContentClass()] = 0; - $classes[$spa->GetContentClass()] += 1; - } - if (empty($classes)) - $checkClasses = "policies only"; - else if (array_sum($classes) > 4) { - $checkClasses = ""; - foreach($classes as $class=>$count) { - if ($count == 1) - $checkClasses .= sprintf("%s ", $class); - else - $checkClasses .= sprintf("%s(%d) ", $class, $count); - } - } - else - $checkClasses = implode(" ", array_keys($classes)); - - $pingTracking = ZPush::GetPingTracking(); - $this->changes = array(); - $changesAvailable = false; - - ZPush::GetDeviceManager()->AnnounceProcessAsPush(); - ZPush::GetTopCollector()->AnnounceInformation(sprintf("lifetime %ds", $lifetime), true); - ZLog::Write(LOGLEVEL_INFO, sprintf("SyncCollections->CheckForChanges(): Waiting for %s changes... (lifetime %d seconds)", (empty($classes))?'policy':'store', $lifetime)); - - // use changes sink where available - $changesSink = false; - $forceRealExport = 0; - // do not create changessink if there are no folders - if (!empty($classes) && ZPush::GetBackend()->HasChangesSink()) { - $changesSink = true; - - // initialize all possible folders - foreach ($this->collections as $folderid => $spa) { - if ($onlyPingable && $spa->GetPingableFlag() !== true) - continue; - - // switch user store if this is a additional folder and initialize sink - ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($folderid)); - if (! ZPush::GetBackend()->ChangesSinkInitialize($folderid)) - throw new StatusException(sprintf("Error initializing ChangesSink for folder id '%s'", $folderid), self::ERROR_WRONG_HIERARCHY); - } - } - - // wait for changes - $started = time(); - $endat = time() + $lifetime; - - // always use policy key from the request if it was sent - $policyKey = $this->GetReferencePolicyKey(); - if (Request::WasPolicyKeySent() && Request::GetPolicyKey() != 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("refpolkey:'%s', sent polkey:'%s'", $policyKey, Request::GetPolicyKey())); - $policyKey = Request::GetPolicyKey(); - } - while(($now = time()) < $endat) { - // how long are we waiting for changes - $this->waitingTime = $now-$started; - - $nextInterval = $interval; - // we should not block longer than the lifetime - if ($endat - $now < $nextInterval) - $nextInterval = $endat - $now; - - // Check if provisioning is necessary - // if a PolicyKey was sent use it. If not, compare with the ReferencePolicyKey - if (PROVISIONING === true && $policyKey !== false && ZPush::GetDeviceManager()->ProvisioningRequired($policyKey, true)) - // the hierarchysync forces provisioning - throw new StatusException("SyncCollections->CheckForChanges(): PolicyKey changed. Provisioning required.", self::ERROR_WRONG_HIERARCHY); - - // Check if a hierarchy sync is necessary - if (ZPush::GetDeviceManager()->IsHierarchySyncRequired()) - throw new StatusException("SyncCollections->CheckForChanges(): HierarchySync required.", self::HIERARCHY_CHANGED); - - // Check if there are newer requests - // If so, this process should be terminated if more than 60 secs to go - if ($pingTracking->DoForcePingTimeout()) { - // do not update CPOs because another process has already read them! - $this->saveData = false; - - // more than 60 secs to go? - if (($now + 60) < $endat) { - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Forced timeout after %ds", ($now-$started)), true); - throw new StatusException(sprintf("SyncCollections->CheckForChanges(): Timeout forced after %ss from %ss due to other process", ($now-$started), $lifetime), self::OBSOLETE_CONNECTION); - } - } - - // Use changes sink if available - if ($changesSink) { - // in some occasions we do realize a full export to see if there are pending changes - // every 5 minutes this is also done to see if there were "missed" notifications - if (SINK_FORCERECHECK !== false && $forceRealExport+SINK_FORCERECHECK <= $now) { - if ($this->CountChanges($onlyPingable)) { - ZLog::Write(LOGLEVEL_DEBUG, "SyncCollections->CheckForChanges(): Using ChangesSink but found relevant changes on regular export"); - return true; - } - $forceRealExport = $now; - } - - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Sink %d/%ds on %s", ($now-$started), $lifetime, $checkClasses)); - $notifications = ZPush::GetBackend()->ChangesSink($nextInterval); - - $validNotifications = false; - foreach ($notifications as $folderid) { - // ZP-631 - temporary disable checking validity of notifications - // notify mobile for all received notifications - $this->changes[$folderid] = 1; - $validNotifications = true; -// // check if the notification on the folder is within our filter -// if ($this->CountChange($folderid)) { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s'", $folderid)); -// $validNotifications = true; -// $this->waitingTime = time()-$started; -// } -// else { -// ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Notification received on folder '%s', but it is not relevant", $folderid)); -// } - } - if ($validNotifications) - return true; - } - // use polling mechanism - else { - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Polling %d/%ds on %s", ($now-$started), $lifetime, $checkClasses)); - if ($this->CountChanges($onlyPingable)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): Found changes polling")); - return true; - } - else { - sleep($nextInterval); - } - } // end polling - } // end wait for changes - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->CheckForChanges(): no changes found after %ds", time() - $started)); - - return false; - } - - /** - * Checks if the currently known collections for - * changes performing Exporter->GetChangeCount() - * - * @param boolean $onlyPingable (opt) only check for folders which have the PingableFlag - * - * @access public - * @return boolean indicating if changes were found or not - */ - public function CountChanges($onlyPingable = false) { - $changesAvailable = false; - foreach ($this->collections as $folderid => $spa) { - if ($onlyPingable && $spa->GetPingableFlag() !== true) - continue; - - if (isset($this->addparms[$spa->GetFolderId()]["status"]) && $this->addparms[$spa->GetFolderId()]["status"] != SYNC_STATUS_SUCCESS) - continue; - - if ($this->CountChange($folderid)) - $changesAvailable = true; - } - - return $changesAvailable; - } - - /** - * Checks a folder for changes performing Exporter->GetChangeCount() - * - * @param string $folderid counts changes for a folder - * - * @access private - * @return boolean indicating if changes were found or not - */ - private function CountChange($folderid) { - $spa = $this->GetCollection($folderid); - - // switch user store if this is a additional folder (additional true -> do not debug) - ZPush::GetBackend()->Setup(ZPush::GetAdditionalSyncFolderStore($folderid, true)); - $changecount = false; - - try { - $exporter = ZPush::GetBackend()->GetExporter($folderid); - if ($exporter !== false && isset($this->addparms[$folderid]["state"])) { - $importer = false; - - $exporter->Config($this->addparms[$folderid]["state"], BACKEND_DISCARD_DATA); - $exporter->ConfigContentParameters($spa->GetCPO()); - $ret = $exporter->InitializeExporter($importer); - - if ($ret !== false) - $changecount = $exporter->GetChangeCount(); - } - } - catch (StatusException $ste) { - throw new StatusException("SyncCollections->CountChange(): exporter can not be re-configured.", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN); - } - - // start over if exporter can not be configured atm - if ($changecount === false ) - ZLog::Write(LOGLEVEL_WARN, "SyncCollections->CountChange(): no changes received from Exporter."); - - $this->changes[$folderid] = $changecount; - - if(isset($this->addparms[$folderid]['savestate'])) { - try { - // Discard any data - while(is_array($exporter->Synchronize())); - $this->addparms[$folderid]['savestate'] = $exporter->GetState(); - } - catch (StatusException $ste) { - throw new StatusException("SyncCollections->CountChange(): could not get new state from exporter", self::ERROR_WRONG_HIERARCHY, null, LOGLEVEL_WARN); - } - } - - return ($changecount > 0); - } - - /** - * Returns an array with all folderid and the amount of changes found - * - * @access public - * @return array - */ - public function GetChangedFolderIds() { - return $this->changes; - } - - /** - * Indicates if there are folders which are pingable - * - * @access public - * @return boolean - */ - public function PingableFolders() { - $pingable = false; - - foreach ($this->collections as $folderid => $spa) { - if ($spa->GetPingableFlag() == true) - $pingable = true; - } - - return $pingable; - } - - /** - * Indicates if the process did wait in a sink, polling or before running a - * regular export to find changes - * - * @access public - * @return array - */ - public function WaitedForChanges() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncCollections->WaitedForChanges: waited for %d seconds", $this->waitingTime)); - return ($this->waitingTime > 0); - } - - /** - * Simple Iterator Interface implementation to traverse through collections - */ - - /** - * Rewind the Iterator to the first element - * - * @access public - * @return - */ - public function rewind() { - return reset($this->collections); - } - - /** - * Returns the current element - * - * @access public - * @return mixed - */ - public function current() { - return current($this->collections); - } - - /** - * Return the key of the current element - * - * @access public - * @return scalar on success, or NULL on failure. - */ - public function key() { - return key($this->collections); - } - - /** - * Move forward to next element - * - * @access public - * @return - */ - public function next() { - return next($this->collections); - } - - /** - * Checks if current position is valid - * - * @access public - * @return boolean - */ - public function valid() { - return (key($this->collections) !== null); - } - - /** - * Gets the StateManager from the DeviceManager - * if it's not available - * - * @access private - * @return - */ - private function loadStateManager() { - if (!isset($this->stateManager)) - $this->stateManager = ZPush::GetDeviceManager()->GetStateManager(); - } -} diff --git a/sources/lib/core/syncparameters.php b/sources/lib/core/syncparameters.php deleted file mode 100644 index ea8deca..0000000 --- a/sources/lib/core/syncparameters.php +++ /dev/null @@ -1,417 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncParameters extends StateObject { - const DEFAULTOPTIONS = "DEFAULT"; - const EMAILOPTIONS = "EMAIL"; - const CALENDAROPTIONS = "CALENDAR"; - const CONTACTOPTIONS = "CONTACTS"; - const NOTEOPTIONS = "NOTES"; - const TASKOPTIONS = "TASKS"; - const SMSOPTIONS = "SMS"; - - private $synckeyChanged = false; - private $currentCPO = self::DEFAULTOPTIONS; - - protected $unsetdata = array( - 'uuid' => false, - 'uuidcounter' => false, - 'uuidnewcounter' => false, - 'folderid' => false, - 'referencelifetime' => 10, - 'lastsynctime' => false, - 'referencepolicykey' => true, - 'pingableflag' => false, - 'contentclass' => false, - 'deletesasmoves' => false, - 'conversationmode' => false, - 'windowsize' => 5, - 'contentparameters' => array(), - 'foldersynctotal' => false, - 'foldersyncremaining' => false, - ); - - /** - * SyncParameters constructor - */ - public function SyncParameters() { - // initialize ContentParameters for the current option - $this->checkCPO(); - } - - - /** - * SyncKey methods - * - * The current and next synckey is saved as uuid and counter - * so partial and ping can access the latest states. - */ - - /** - * Returns the latest SyncKey of this folder - * - * @access public - * @return string/boolean false if no uuid/counter available - */ - public function GetSyncKey() { - if (isset($this->uuid) && isset($this->uuidCounter)) - return StateManager::BuildStateKey($this->uuid, $this->uuidCounter); - - return false; - } - - /** - * Sets the the current synckey. - * This is done by parsing it and saving uuid and counter. - * By setting the current key, the "next" key is obsolete - * - * @param string $synckey - * - * @access public - * @return boolean - */ - public function SetSyncKey($synckey) { - list($this->uuid, $this->uuidCounter) = StateManager::ParseStateKey($synckey); - - // remove newSyncKey - unset($this->uuidNewCounter); - - return true; - } - - /** - * Indicates if this folder has a synckey - * - * @access public - * @return booleans - */ - public function HasSyncKey() { - return (isset($this->uuid) && isset($this->uuidCounter)); - } - - /** - * Sets the the next synckey. - * This is done by parsing it and saving uuid and next counter. - * if the folder has no synckey until now (new sync), the next counter becomes current asl well. - * - * @param string $synckey - * - * @access public - * @throws FatalException if the uuids of current and next do not match - * @return boolean - */ - public function SetNewSyncKey($synckey) { - list($uuid, $uuidNewCounter) = StateManager::ParseStateKey($synckey); - if (!$this->HasSyncKey()) { - $this->uuid = $uuid; - $this->uuidCounter = $uuidNewCounter; - } - else if ($uuid !== $this->uuid) - throw new FatalException("SyncParameters->SetNewSyncKey(): new SyncKey must have the same UUID as current SyncKey"); - - $this->uuidNewCounter = $uuidNewCounter; - $this->synckeyChanged = true; - } - - /** - * Returns the next synckey - * - * @access public - * @return string/boolean returns false if uuid or counter are not available - */ - public function GetNewSyncKey() { - if (isset($this->uuid) && isset($this->uuidNewCounter)) - return StateManager::BuildStateKey($this->uuid, $this->uuidNewCounter); - - return false; - } - - /** - * Indicates if the folder has a next synckey - * - * @access public - * @return boolean - */ - public function HasNewSyncKey() { - return (isset($this->uuid) && isset($this->uuidNewCounter)); - } - - /** - * Return the latest synckey. - * When this is called the new key becomes the current key (if a new key is available). - * The current key is then returned. - * - * @access public - * @return string - */ - public function GetLatestSyncKey() { - // New becomes old - if ($this->HasUuidNewCounter()) { - $this->uuidCounter = $this->uuidNewCounter; - unset($this->uuidNewCounter); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncParameters->GetLatestSyncKey(): '%s'", $this->GetSyncKey())); - return $this->GetSyncKey(); - } - - /** - * Removes the saved SyncKey of this folder - * - * @access public - * @return boolean - */ - public function RemoveSyncKey() { - if (isset($this->uuid)) - unset($this->uuid); - - if (isset($this->uuidCounter)) - unset($this->uuidCounter); - - if (isset($this->uuidNewCounter)) - unset($this->uuidNewCounter); - - ZLog::Write(LOGLEVEL_DEBUG, "SyncParameters->RemoveSyncKey(): saved sync key removed"); - return true; - } - - - /** - * CPO methods - * - * A sync request can have several options blocks. Each block is saved into an own CPO object - * - */ - - /** - * Returns the a specified CPO - * - * @param string $options (opt) If not specified, the default Options (CPO) will be used - * Valid option SyncParameters::SMSOPTIONS (string "SMS") - * - * @access public - * @return ContentParameters object - */ - public function GetCPO($options = self::DEFAULTOPTIONS) { - $options = strtoupper($options); - $this->isValidType($options); - $options = $this->normalizeType($options); - - $this->checkCPO($options); - - // copy contentclass and conversationmode to the CPO - $this->contentParameters[$options]->SetContentClass($this->contentclass); - $this->contentParameters[$options]->SetConversationMode($this->conversationmode); - - return $this->contentParameters[$options]; - } - - /** - * Use the submitted CPO type for next setters/getters - * - * @param string $options (opt) If not specified, the default Options (CPO) will be used - * Valid option SyncParameters::SMSOPTIONS (string "SMS") - * - * @access public - * @return - */ - public function UseCPO($options = self::DEFAULTOPTIONS) { - $options = strtoupper($options); - $this->isValidType($options); - - // remove potential old default CPO if available - if (isset($this->contentParameters[self::DEFAULTOPTIONS]) && $options != self::DEFAULTOPTIONS && $options !== self::SMSOPTIONS) { - $a = $this->contentParameters; - unset($a[self::DEFAULTOPTIONS]); - $this->contentParameters = $a; - ZLog::Write(LOGLEVEL_DEBUG, "SyncParameters->UseCPO(): removed existing DEFAULT CPO as it is obsolete"); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncParameters->UseCPO('%s')", $options)); - $this->currentCPO = $options; - $this->checkCPO($this->currentCPO); - } - - /** - * Checks if a CPO is correctly inicialized and inicializes it if necessary - * - * @param string $options (opt) If not specified, the default Options (CPO) will be used - * Valid option SyncParameters::SMSOPTIONS (string "SMS") - * - * @access private - * @return boolean - */ - private function checkCPO($options = self::DEFAULTOPTIONS) { - $this->isValidType($options); - - if (!isset($this->contentParameters[$options])) { - $a = $this->contentParameters; - $a[$options] = new ContentParameters(); - $this->contentParameters = $a; - } - - return true; - } - - /** - * Checks if the requested option type is available - * - * @param string $options CPO type - * - * @access private - * @return boolean - * @throws FatalNotImplementedException - */ - private function isValidType($options) { - if ($options !== self::DEFAULTOPTIONS && - $options !== self::EMAILOPTIONS && - $options !== self::CALENDAROPTIONS && - $options !== self::CONTACTOPTIONS && - $options !== self::NOTEOPTIONS && - $options !== self::TASKOPTIONS && - $options !== self::SMSOPTIONS) - throw new FatalNotImplementedException(sprintf("SyncParameters->isAllowedType('%s') ContentParameters is invalid. Such type is not available.", $options)); - - return true; - } - - /** - * Normalizes the requested option type and returns it as - * default option if no default is available - * - * @param string $options CPO type - * - * @access private - * @return string - * @throws FatalNotImplementedException - */ - private function normalizeType($options) { - // return the requested CPO as it is defined - if (isset($this->contentParameters[$options])) - return $options; - - $returnCPO = $options; - // return email, calendar, contact or note CPO as default CPO if there no explicit default CPO defined - if ($options == self::DEFAULTOPTIONS && !isset($this->contentParameters[self::DEFAULTOPTIONS])) { - - if (isset($this->contentParameters[self::EMAILOPTIONS])) - $returnCPO = self::EMAILOPTIONS; - elseif (isset($this->contentParameters[self::CALENDAROPTIONS])) - $returnCPO = self::CALENDAROPTIONS; - elseif (isset($this->contentParameters[self::CONTACTOPTIONS])) - $returnCPO = self::CONTACTOPTIONS; - elseif (isset($this->contentParameters[self::NOTEOPTIONS])) - $returnCPO = self::NOTEOPTIONS; - elseif (isset($this->contentParameters[self::TASKOPTIONS])) - $returnCPO = self::TASKOPTIONS; - - return $returnCPO; - } - // something unexpected happened, just return default, empty in the worst case - else { - ZLog::Write(LOGLEVEL_WARN, "SyncParameters->normalizeType(): no DEFAULT CPO available, creating empty CPO"); - $this->checkCPO(self::DEFAULTOPTIONS); - return self::DEFAULTOPTIONS; - } - } - - - /** - * PHP magic to implement any getter, setter, has and delete operations - * on an instance variable. - * - * NOTICE: All magic getters and setters of this object which are not defined in the unsetdata array are passed to the current CPO. - * - * Methods like e.g. "SetVariableName($x)" and "GetVariableName()" are supported - * - * @access public - * @return mixed - */ - public function __call($name, $arguments) { - $lowname = strtolower($name); - $operator = substr($lowname, 0,3); - $var = substr($lowname,3); - - if (array_key_exists($var, $this->unsetdata)) { - return parent::__call($name, $arguments); - } - - return $this->contentParameters[$this->currentCPO]->__call($name, $arguments); - } - - - /** - * un/serialization methods - */ - - /** - * Called before the StateObject is serialized - * - * @access protected - * @return boolean - */ - protected function preSerialize() { - parent::preSerialize(); - - if ($this->changed === true && ($this->synckeyChanged || $this->lastsynctime === false)) - $this->lastsynctime = time(); - - return true; - } - - /** - * Called after the StateObject was unserialized - * - * @access protected - * @return boolean - */ - protected function postUnserialize() { - // init with the available CPO or default - $availableCPO = $this->normalizeType(self::DEFAULTOPTIONS); - $this->UseCPO($availableCPO); - - return true; - } -} diff --git a/sources/lib/core/zlog.php b/sources/lib/core/zlog.php deleted file mode 100644 index 26b03b4..0000000 --- a/sources/lib/core/zlog.php +++ /dev/null @@ -1,291 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ZLog { - static private $devid = ''; - static private $user = ''; - static private $authUser = false; - static private $pidstr; - static private $wbxmlDebug = ''; - static private $lastLogs = array(); - static private $userLog = false; - static private $unAuthCache = array(); - static private $syslogEnabled = false; - - /** - * Initializes the logging - * - * @access public - * @return boolean - */ - static public function Initialize() { - global $specialLogUsers; - - if (defined('LOG_SYSLOG_ENABLED') && LOG_SYSLOG_ENABLED) { - self::$syslogEnabled = true; - ZSyslog::Initialize(); - } - - // define some constants for the logging - if (!defined('LOGUSERLEVEL')) - define('LOGUSERLEVEL', LOGLEVEL_OFF); - - if (!defined('LOGLEVEL')) - define('LOGLEVEL', LOGLEVEL_OFF); - - list($user,) = Utils::SplitDomainUser(strtolower(Request::GetGETUser())); - self::$userLog = in_array($user, $specialLogUsers); - if (!defined('WBXML_DEBUG') && $user) { - // define the WBXML_DEBUG mode on user basis depending on the configurations - if (LOGLEVEL >= LOGLEVEL_WBXML || (LOGUSERLEVEL >= LOGLEVEL_WBXML && self::$userLog)) - define('WBXML_DEBUG', true); - else - define('WBXML_DEBUG', false); - } - - if ($user) - self::$user = '['. $user .'] '; - else - self::$user = ''; - - // log the device id if the global loglevel is set to log devid or the user is in and has the right log level - if (Request::GetDeviceID() != "" && (LOGLEVEL >= LOGLEVEL_DEVICEID || (LOGUSERLEVEL >= LOGLEVEL_DEVICEID && self::$userLog))) - self::$devid = '['. Request::GetDeviceID() .'] '; - else - self::$devid = ''; - - return true; - } - - /** - * Writes a log line - * - * @param int $loglevel one of the defined LOGLEVELS - * @param string $message - * @param boolean $truncate indicate if the message should be truncated, default true - * - * @access public - * @return - */ - static public function Write($loglevel, $message, $truncate = true) { - // truncate messages longer than 10 KB - $messagesize = strlen($message); - if ($truncate && $messagesize > 10240) - $message = substr($message, 0, 10240) . sprintf(" ", $messagesize); - - self::$lastLogs[$loglevel] = $message; - $data = self::buildLogString($loglevel) . $message . "\n"; - - if ($loglevel <= LOGLEVEL) { - self::writeToLog($loglevel, $data, LOGFILE); - } - - // should we write this into the user log? - if ($loglevel <= LOGUSERLEVEL && self::$userLog) { - // padd level for better reading - $data = str_replace(self::getLogLevelString($loglevel), self::getLogLevelString($loglevel,true), $data); - - // is the user authenticated? - if (self::logToUserFile()) { - // something was logged before the user was authenticated, write this to the log - if (!empty(self::$unAuthCache)) { - self::writeToLog($loglevel, implode('', self::$unAuthCache), LOGFILEDIR . self::logToUserFile() . ".log"); - self::$unAuthCache = array(); - } - // only use plain old a-z characters for the generic log file - self::writeToLog($loglevel, $data, LOGFILEDIR . self::logToUserFile() . ".log"); - } - // the user is not authenticated yet, we save the log into memory for now - else { - self::$unAuthCache[] = $data; - } - } - - if (($loglevel & LOGLEVEL_FATAL) || ($loglevel & LOGLEVEL_ERROR)) { - self::writeToLog($loglevel, $data, LOGERRORFILE); - } - - if ($loglevel & LOGLEVEL_WBXMLSTACK) { - self::$wbxmlDebug .= $message. "\n"; - } - } - - /** - * Returns logged information about the WBXML stack - * - * @access public - * @return string - */ - static public function GetWBXMLDebugInfo() { - return trim(self::$wbxmlDebug); - } - - /** - * Returns the last message logged for a log level - * - * @param int $loglevel one of the defined LOGLEVELS - * - * @access public - * @return string/false returns false if there was no message logged in that level - */ - static public function GetLastMessage($loglevel) { - return (isset(self::$lastLogs[$loglevel]))?self::$lastLogs[$loglevel]:false; - } - - - /** - * Writes info at the end of the request but only if the LOGLEVEL is DEBUG or more verbose - * - * @access public - * @return - */ - static public function WriteEnd() { - if (LOGLEVEL_DEBUG <= LOGLEVEL) { - if (version_compare(phpversion(), '5.4.0') < 0) { - $time_used = number_format(time() - $_SERVER["REQUEST_TIME"], 4); - } - else { - $time_used = number_format(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 4); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Memory usage information: %s/%s - Execution time: %s - HTTP responde code: %s", memory_get_peak_usage(false), memory_get_peak_usage(true), $time_used, http_response_code())); - ZLog::Write(LOGLEVEL_DEBUG, "-------- End"); - } - } - - /**---------------------------------------------------------------------------------------------------------- - * private log stuff - */ - - /** - * Returns the filename logs for a WBXML debug log user should be saved to - * - * @access private - * @return string - */ - static private function logToUserFile() { - global $specialLogUsers; - - if (self::$authUser === false) { - if (RequestProcessor::isUserAuthenticated()) { - $authuser = Request::GetAuthUser(); - if ($authuser && in_array($authuser, $specialLogUsers)) - self::$authUser = preg_replace('/[^a-z0-9]/', '_', strtolower($authuser)); - } - } - return self::$authUser; - } - - /** - * Returns the string to be logged - * - * @access private - * @return string - */ - static private function buildLogString($loglevel) { - if (!isset(self::$pidstr)) - self::$pidstr = '[' . str_pad(@getmypid(),5," ",STR_PAD_LEFT) . '] '; - - if (!isset(self::$user)) - self::$user = ''; - - if (!isset(self::$devid)) - self::$devid = ''; - - if (self::$syslogEnabled) - return self::$pidstr . self::getLogLevelString($loglevel, (LOGLEVEL > LOGLEVEL_INFO)) . " " . self::$user . self::$devid; - else - return Utils::GetFormattedTime() . " " . self::$pidstr . self::getLogLevelString($loglevel, (LOGLEVEL > LOGLEVEL_INFO)) . " " . self::$user . self::$devid; - } - - /** - * Returns the string representation of the LOGLEVEL. - * String can be padded - * - * @param int $loglevel one of the LOGLEVELs - * @param boolean $pad - * - * @access private - * @return string - */ - static private function getLogLevelString($loglevel, $pad = false) { - if ($pad) $s = " "; - else $s = ""; - switch($loglevel) { - case LOGLEVEL_OFF: return ""; break; - case LOGLEVEL_FATAL: return "[FATAL]"; break; - case LOGLEVEL_ERROR: return "[ERROR]"; break; - case LOGLEVEL_WARN: return "[".$s."WARN]"; break; - case LOGLEVEL_INFO: return "[".$s."INFO]"; break; - case LOGLEVEL_DEBUG: return "[DEBUG]"; break; - case LOGLEVEL_WBXML: return "[WBXML]"; break; - case LOGLEVEL_DEVICEID: return "[DEVICEID]"; break; - case LOGLEVEL_WBXMLSTACK: return "[WBXMLSTACK]"; break; - } - } - - /** - * Write the message to the log facility. - * - * @param int $loglevel - * @param string $data - * @param string $logfile - * - * @access private - * @return void - */ - static private function writeToLog($loglevel, $data, $logfile = null) { - if (self::$syslogEnabled) { - if (ZSyslog::send($loglevel, $data) === false) { - error_log("Unable to send to syslog"); - error_log($data); - } - } - else { - if (@file_put_contents($logfile, $data, FILE_APPEND) === false) { - error_log(sprintf("Unable to write in %s", $logfile)); - error_log($data); - } - } - } -} diff --git a/sources/lib/core/zpush-utils.php b/sources/lib/core/zpush-utils.php deleted file mode 100644 index 92f0bcc..0000000 --- a/sources/lib/core/zpush-utils.php +++ /dev/null @@ -1,73 +0,0 @@ - 7 -//STORE_SUPPORTS_UNICODE is true and the convertion will not be done -//for other backends. -function utf8_to_windows1252($string, $option = "", $force_convert = false) { - //if the store supports unicode return the string without converting it - if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) return $string; - - if (function_exists("iconv")){ - return @iconv("UTF-8", "Windows-1252" . $option, $string); - }else{ - return utf8_decode($string); // no euro support here - } -} - -function windows1252_to_utf8($string, $option = "", $force_convert = false) { - //if the store supports unicode return the string without converting it - if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE == true) return $string; - - if (function_exists("iconv")){ - return @iconv("Windows-1252", "UTF-8" . $option, $string); - }else{ - return utf8_encode($string); // no euro support here - } -} - -function w2u($string) { return windows1252_to_utf8($string); } -function u2w($string) { return utf8_to_windows1252($string); } - -function w2ui($string) { return windows1252_to_utf8($string, "//TRANSLIT"); } -function u2wi($string) { return utf8_to_windows1252($string, "//TRANSLIT"); } - -/** - * @param string $message - * @deprecated - */ -function debugLog($message) { - ZLog::Write(LOGLEVEL_DEBUG, $message); -} - -// TODO review error handler -function zarafa_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { - $bt = debug_backtrace(); - switch ($errno) { - case 8192: // E_DEPRECATED since PHP 5.3.0 - // do not handle this message - break; - - case E_NOTICE: - case E_WARNING: - // TODO check if there is a better way to avoid these messages - if (stripos($errfile,'interprocessdata') !== false && stripos($errstr,'shm_get_var()') !== false) - break; - ZLog::Write(LOGLEVEL_WARN, "$errfile:$errline $errstr ($errno)"); - break; - - default: - ZLog::Write(LOGLEVEL_ERROR, "trace error: $errfile:$errline $errstr ($errno) - backtrace: ". (count($bt)-1) . " steps"); - for($i = 1, $bt_length = count($bt); $i < $bt_length; $i++) { - $file = $line = "unknown"; - if (isset($bt[$i]['file'])) $file = $bt[$i]['file']; - if (isset($bt[$i]['line'])) $line = $bt[$i]['line']; - ZLog::Write(LOGLEVEL_ERROR, "trace: $i:". $file . ":" . $line. " - " . ((isset($bt[$i]['class']))? $bt[$i]['class'] . $bt[$i]['type']:""). $bt[$i]['function']. "()"); - } - //throw new Exception("An error occured."); - break; - } -} - -error_reporting(E_ALL); -set_error_handler("zarafa_error_handler"); diff --git a/sources/lib/core/zpush.php b/sources/lib/core/zpush.php deleted file mode 100644 index 463b79b..0000000 --- a/sources/lib/core/zpush.php +++ /dev/null @@ -1,876 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class ZPush { - const UNAUTHENTICATED = 1; - const UNPROVISIONED = 2; - const NOACTIVESYNCCOMMAND = 3; - const WEBSERVICECOMMAND = 4; - const HIERARCHYCOMMAND = 5; - const PLAININPUT = 6; - const REQUESTHANDLER = 7; - const CLASS_NAME = 1; - const CLASS_REQUIRESPROTOCOLVERSION = 2; - const CLASS_DEFAULTTYPE = 3; - const CLASS_OTHERTYPES = 4; - - // AS versions - const ASV_25 = "2.5"; - const ASV_12 = "12.0"; - const ASV_121 = "12.1"; - const ASV_14 = "14.0"; - const ASV_141 = "14.1"; - - /** - * Command codes for base64 encoded requests (AS >= 12.1) - */ - const COMMAND_SYNC = 0; - const COMMAND_SENDMAIL = 1; - const COMMAND_SMARTFORWARD = 2; - const COMMAND_SMARTREPLY = 3; - const COMMAND_GETATTACHMENT = 4; - const COMMAND_FOLDERSYNC = 9; - const COMMAND_FOLDERCREATE = 10; - const COMMAND_FOLDERDELETE = 11; - const COMMAND_FOLDERUPDATE = 12; - const COMMAND_MOVEITEMS = 13; - const COMMAND_GETITEMESTIMATE = 14; - const COMMAND_MEETINGRESPONSE = 15; - const COMMAND_SEARCH = 16; - const COMMAND_SETTINGS = 17; - const COMMAND_PING = 18; - const COMMAND_ITEMOPERATIONS = 19; - const COMMAND_PROVISION = 20; - const COMMAND_RESOLVERECIPIENTS = 21; - const COMMAND_VALIDATECERT = 22; - - // Deprecated commands (AS >= 14) - const COMMAND_GETHIERARCHY = -1; - - // Webservice commands - const COMMAND_WEBSERVICE_DEVICE = -100; - const COMMAND_WEBSERVICE_USERS = -101; - - // Latest supported State version - const STATE_VERSION = IStateMachine::STATEVERSION_02; - - static private $autoloadBackendPreference = array( - "BackendZarafa", - "BackendCombined", - "BackendIMAP", - "BackendVCardDir", - "BackendMaildir" - ); - - // Versions 1.0, 2.0, 2.1 and 2.5 are deprecated (ZP-604) - static private $supportedASVersions = array( - self::ASV_12, - self::ASV_121, - self::ASV_14, - self::ASV_141 - ); - - static private $supportedCommands = array( - // COMMAND // AS VERSION // OTHER SETTINGS - self::COMMAND_SYNC => array(self::ASV_25), - self::COMMAND_SENDMAIL => array(self::ASV_25), - self::COMMAND_SMARTFORWARD => array(self::ASV_25), - self::COMMAND_SMARTREPLY => array(self::ASV_25), - self::COMMAND_GETATTACHMENT => array(self::ASV_25), - self::COMMAND_GETHIERARCHY => array(self::ASV_25, self::HIERARCHYCOMMAND), // deprecated (AS >= 14) - self::COMMAND_FOLDERSYNC => array(self::ASV_25, self::HIERARCHYCOMMAND), - self::COMMAND_FOLDERCREATE => array(self::ASV_25, self::HIERARCHYCOMMAND), - self::COMMAND_FOLDERDELETE => array(self::ASV_25, self::HIERARCHYCOMMAND), - self::COMMAND_FOLDERUPDATE => array(self::ASV_25, self::HIERARCHYCOMMAND), - self::COMMAND_MOVEITEMS => array(self::ASV_25), - self::COMMAND_GETITEMESTIMATE => array(self::ASV_25), - self::COMMAND_MEETINGRESPONSE => array(self::ASV_25), - self::COMMAND_RESOLVERECIPIENTS => array(self::ASV_25), - self::COMMAND_VALIDATECERT => array(self::ASV_25), - self::COMMAND_PROVISION => array(self::ASV_25, self::UNAUTHENTICATED, self::UNPROVISIONED), - self::COMMAND_SEARCH => array(self::ASV_25), - self::COMMAND_PING => array(self::ASV_25, self::UNPROVISIONED), - self::COMMAND_ITEMOPERATIONS => array(self::ASV_12), - self::COMMAND_SETTINGS => array(self::ASV_12), - - self::COMMAND_WEBSERVICE_DEVICE => array(self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND), - self::COMMAND_WEBSERVICE_USERS => array(self::PLAININPUT, self::NOACTIVESYNCCOMMAND, self::WEBSERVICECOMMAND), - ); - - - static private $requestHandler = array( - // COMMAND // REQUESTHANDLER - self::COMMAND_SYNC => "Sync", - self::COMMAND_SENDMAIL => "SendMail", - self::COMMAND_SMARTFORWARD => "SendMail", - self::COMMAND_SMARTREPLY => "SendMail", - self::COMMAND_GETATTACHMENT => "GetAttachment", - self::COMMAND_GETHIERARCHY => "GetHierarchy", // deprecated (AS >= 14) - self::COMMAND_FOLDERSYNC => "FolderSync", - self::COMMAND_FOLDERCREATE => "FolderChange", - self::COMMAND_FOLDERDELETE => "FolderChange", - self::COMMAND_FOLDERUPDATE => "FolderChange", - self::COMMAND_MOVEITEMS => "MoveItems", - self::COMMAND_GETITEMESTIMATE => "GetItemEstimate", - self::COMMAND_MEETINGRESPONSE => "MeetingResponse", - self::COMMAND_RESOLVERECIPIENTS => "ResolveRecipients", - self::COMMAND_VALIDATECERT => "ValidateCert", - self::COMMAND_PROVISION => "Provisioning", - self::COMMAND_SEARCH => "Search", - self::COMMAND_PING => "Ping", - self::COMMAND_ITEMOPERATIONS => "ItemOperations", - self::COMMAND_SETTINGS => "Settings", - - self::COMMAND_WEBSERVICE_DEVICE => "Webservice", - self::COMMAND_WEBSERVICE_USERS => "Webservice", - ); - - static private $classes = array( - "Email" => array( - self::CLASS_NAME => "SyncMail", - self::CLASS_REQUIRESPROTOCOLVERSION => false, - self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_INBOX, - self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_OTHER, SYNC_FOLDER_TYPE_DRAFTS, SYNC_FOLDER_TYPE_WASTEBASKET, - SYNC_FOLDER_TYPE_SENTMAIL, SYNC_FOLDER_TYPE_OUTBOX, SYNC_FOLDER_TYPE_USER_MAIL, - SYNC_FOLDER_TYPE_JOURNAL, SYNC_FOLDER_TYPE_USER_JOURNAL), - ), - "Contacts" => array( - self::CLASS_NAME => "SyncContact", - self::CLASS_REQUIRESPROTOCOLVERSION => true, - self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_CONTACT, - self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_CONTACT), - ), - "Calendar" => array( - self::CLASS_NAME => "SyncAppointment", - self::CLASS_REQUIRESPROTOCOLVERSION => false, - self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_APPOINTMENT, - self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_APPOINTMENT), - ), - "Tasks" => array( - self::CLASS_NAME => "SyncTask", - self::CLASS_REQUIRESPROTOCOLVERSION => false, - self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_TASK, - self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_TASK), - ), - "Notes" => array( - self::CLASS_NAME => "SyncNote", - self::CLASS_REQUIRESPROTOCOLVERSION => false, - self::CLASS_DEFAULTTYPE => SYNC_FOLDER_TYPE_NOTE, - self::CLASS_OTHERTYPES => array(SYNC_FOLDER_TYPE_USER_NOTE), - ), - ); - - - static private $stateMachine; - static private $searchProvider; - static private $deviceManager; - static private $topCollector; - static private $backend; - static private $addSyncFolders; - - - /** - * Verifies configuration - * - * @access public - * @return boolean - * @throws FatalMisconfigurationException - */ - static public function CheckConfig() { - // check the php version - if (version_compare(phpversion(), '5.3.0') < 0) - throw new FatalException("The configured PHP version is too old. Please make sure at least PHP 5.3 is used."); - - // some basic checks - if (!defined('BASE_PATH')) - throw new FatalMisconfigurationException("The BASE_PATH is not configured. Check if the config.php file is in place."); - - if (substr(BASE_PATH, -1,1) != "/") - throw new FatalMisconfigurationException("The BASE_PATH should terminate with a '/'"); - - if (!file_exists(BASE_PATH)) - throw new FatalMisconfigurationException("The configured BASE_PATH does not exist or can not be accessed."); - - if (defined('BASE_PATH_CLI') && file_exists(BASE_PATH_CLI)) - define('REAL_BASE_PATH', BASE_PATH_CLI); - else - define('REAL_BASE_PATH', BASE_PATH); - - if (!defined('LOGFILEDIR')) - throw new FatalMisconfigurationException("The LOGFILEDIR is not configured. Check if the config.php file is in place."); - - if (substr(LOGFILEDIR, -1,1) != "/") - throw new FatalMisconfigurationException("The LOGFILEDIR should terminate with a '/'"); - - if (!file_exists(LOGFILEDIR)) - throw new FatalMisconfigurationException("The configured LOGFILEDIR does not exist or can not be accessed."); - - if ((!file_exists(LOGFILE) && !touch(LOGFILE)) || !is_writable(LOGFILE)) - throw new FatalMisconfigurationException("The configured LOGFILE can not be modified."); - - if ((!file_exists(LOGERRORFILE) && !touch(LOGERRORFILE)) || !is_writable(LOGERRORFILE)) - throw new FatalMisconfigurationException("The configured LOGERRORFILE can not be modified."); - - // check ownership on the (eventually) just created files - Utils::FixFileOwner(LOGFILE); - Utils::FixFileOwner(LOGERRORFILE); - - // set time zone - // code contributed by Robert Scheck (rsc) - more information: https://developer.berlios.de/mantis/view.php?id=479 - if(function_exists("date_default_timezone_set")) { - if(defined('TIMEZONE') ? constant('TIMEZONE') : false) { - if (! @date_default_timezone_set(TIMEZONE)) - throw new FatalMisconfigurationException(sprintf("The configured TIMEZONE '%s' is not valid. Please check supported timezones at http://www.php.net/manual/en/timezones.php", constant('TIMEZONE'))); - } - else if(!ini_get('date.timezone')) { - date_default_timezone_set('Europe/Amsterdam'); - } - } - - return true; - } - - /** - * Verifies Timezone, StateMachine and Backend configuration - * - * @access public - * @return boolean - * @trows FatalMisconfigurationException - */ - static public function CheckAdvancedConfig() { - global $specialLogUsers, $additionalFolders; - - if (!is_array($specialLogUsers)) - throw new FatalMisconfigurationException("The WBXML log users is not an array."); - - if (!defined('SINK_FORCERECHECK')) { - define('SINK_FORCERECHECK', 300); - } - else if (SINK_FORCERECHECK !== false && (!is_int(SINK_FORCERECHECK) || SINK_FORCERECHECK < 1)) - throw new FatalMisconfigurationException("The SINK_FORCERECHECK value must be 'false' or a number higher than 0."); - - if (!defined('SYNC_CONTACTS_MAXPICTURESIZE')) { - define('SYNC_CONTACTS_MAXPICTURESIZE', 49152); - } - else if ((!is_int(SYNC_CONTACTS_MAXPICTURESIZE) || SYNC_CONTACTS_MAXPICTURESIZE < 1)) - throw new FatalMisconfigurationException("The SYNC_CONTACTS_MAXPICTURESIZE value must be a number higher than 0."); - - if (!defined('USE_PARTIAL_FOLDERSYNC')) { - define('USE_PARTIAL_FOLDERSYNC', false); - } - - // the check on additional folders will not throw hard errors, as this is probably changed on live systems - if (isset($additionalFolders) && !is_array($additionalFolders)) - ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : The additional folders synchronization not available as array."); - else { - self::$addSyncFolders = array(); - - // process configured data - foreach ($additionalFolders as $af) { - - if (!is_array($af) || !isset($af['store']) || !isset($af['folderid']) || !isset($af['name']) || !isset($af['type'])) { - ZLog::Write(LOGLEVEL_ERROR, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Missing parameters. Entry will be ignored."); - continue; - } - - if ($af['store'] == "" || $af['folderid'] == "" || $af['name'] == "" || $af['type'] == "") { - ZLog::Write(LOGLEVEL_WARN, "ZPush::CheckConfig() : the additional folder synchronization is not configured correctly. Empty parameters. Entry will be ignored."); - continue; - } - - if (!in_array($af['type'], array(SYNC_FOLDER_TYPE_USER_CONTACT, SYNC_FOLDER_TYPE_USER_APPOINTMENT, SYNC_FOLDER_TYPE_USER_TASK, SYNC_FOLDER_TYPE_USER_MAIL))) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPush::CheckConfig() : the type of the additional synchronization folder '%s is not permitted.", $af['name'])); - continue; - } - - $folder = new SyncFolder(); - $folder->serverid = $af['folderid']; - $folder->parentid = 0; // only top folders are supported - $folder->displayname = $af['name']; - $folder->type = $af['type']; - // save store as custom property which is not streamed directly to the device - $folder->NoBackendFolder = true; - $folder->Store = $af['store']; - self::$addSyncFolders[$folder->serverid] = $folder; - } - - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Used timezone '%s'", date_default_timezone_get())); - - // get the statemachine, which will also try to load the backend.. This could throw errors - self::GetStateMachine(); - } - - /** - * Returns the StateMachine object - * which has to be an IStateMachine implementation - * - * @access public - * @throws FatalNotImplementedException - * @throws HTTPReturnCodeException - * @return object implementation of IStateMachine - */ - static public function GetStateMachine() { - if (!isset(ZPush::$stateMachine)) { - // the backend could also return an own IStateMachine implementation - $backendStateMachine = self::GetBackend()->GetStateMachine(); - - // if false is returned, use the default StateMachine - if ($backendStateMachine !== false) { - ZLog::Write(LOGLEVEL_DEBUG, "Backend implementation of IStateMachine: ".get_class($backendStateMachine)); - if (in_array('IStateMachine', class_implements($backendStateMachine))) - ZPush::$stateMachine = $backendStateMachine; - else - throw new FatalNotImplementedException("State machine returned by the backend does not implement the IStateMachine interface!"); - } - else { - // Initialize the default StateMachine - if (defined('STATE_MACHINE') && STATE_MACHINE == 'SQL') { - ZPush::$stateMachine = new SqlStateMachine(); - } - else { - ZPush::$stateMachine = new FileStateMachine(); - } - } - - if (ZPush::$stateMachine->GetStateVersion() !== ZPush::GetLatestStateVersion()) { - if (class_exists("TopCollector")) self::GetTopCollector()->AnnounceInformation("Run migration script!", true); - throw new HTTPReturnCodeException(sprintf("The state version available to the %s is not the latest version - please run the state upgrade script. See release notes for more information.", get_class(ZPush::$stateMachine)), HTTP_CODE_500); - } - } - return ZPush::$stateMachine; - } - - /** - * Returns the latest version of supported states - * - * @access public - * @return int - */ - static public function GetLatestStateVersion() { - return self::STATE_VERSION; - } - - /** - * Returns the DeviceManager object - * - * @param boolean $initialize (opt) default true: initializes the DeviceManager if not already done - * - * @access public - * @return object DeviceManager - */ - static public function GetDeviceManager($initialize = true) { - if (!isset(ZPush::$deviceManager) && $initialize) - ZPush::$deviceManager = new DeviceManager(); - - return ZPush::$deviceManager; - } - - /** - * Returns the Top data collector object - * - * @access public - * @return object TopCollector - */ - static public function GetTopCollector() { - if (!isset(self::$topCollector)) { - $class = defined('TOP_COLLECTOR_BACKEND') ? TOP_COLLECTOR_BACKEND : 'TopCollector'; - self::$topCollector = new $class(); - } - return self::$topCollector; - } - - /** - * Returns an instance of PingTracking - * - * @access public - * @return object IPingTracking - */ - static public function GetPingTracking() { - $class = defined('PING_TRACKING_BACKEND') ? PING_TRACKING_BACKEND : 'PingTracking'; - return new $class(); - } - - /** - * Returns an instance of LoopDetection - * - * @access public - * @return object ILoopDetection - */ - static public function GetLoopDetection() { - $class = defined('LOOP_DETECTION_BACKEND') ? LOOP_DETECTION_BACKEND : 'LoopDetection'; - return new $class(); - } - - /** - * Loads a backend file - * - * @param string $backendname - - * @access public - * @throws FatalNotImplementedException - * @return boolean - */ - static public function IncludeBackend($backendname) { - if ($backendname == false) return false; - - $backendname = strtolower($backendname); - if (substr($backendname, 0, 7) !== 'backend') - throw new FatalNotImplementedException(sprintf("Backend '%s' is not allowed",$backendname)); - - $rbn = substr($backendname, 7); - - $subdirbackend = REAL_BASE_PATH . "backend/" . $rbn . "/" . $rbn . ".php"; - $stdbackend = REAL_BASE_PATH . "backend/" . $rbn . ".php"; - - if (is_file($subdirbackend)) - $toLoad = $subdirbackend; - else if (is_file($stdbackend)) - $toLoad = $stdbackend; - else - return false; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Including backend file: '%s'", $toLoad)); - include_once($toLoad); - return true; - } - - /** - * Returns the SearchProvider object - * which has to be an ISearchProvider implementation - * - * @access public - * @return object implementation of ISearchProvider - * @throws FatalMisconfigurationException, FatalNotImplementedException - */ - static public function GetSearchProvider() { - if (!isset(ZPush::$searchProvider)) { - // is a global searchprovider configured ? It will outrank the backend - if (defined('SEARCH_PROVIDER') && @constant('SEARCH_PROVIDER') != "") { - $searchClass = @constant('SEARCH_PROVIDER'); - - if (! class_exists($searchClass)) - self::IncludeBackend($searchClass); - - if (class_exists($searchClass)) - $aSearchProvider = new $searchClass(); - else - throw new FatalMisconfigurationException(sprintf("Search provider '%s' can not be loaded. Check configuration!", $searchClass)); - } - // get the searchprovider from the backend - else - $aSearchProvider = self::GetBackend()->GetSearchProvider(); - - if (in_array('ISearchProvider', class_implements($aSearchProvider))) - ZPush::$searchProvider = $aSearchProvider; - else - throw new FatalNotImplementedException("Instantiated SearchProvider does not implement the ISearchProvider interface!"); - } - return ZPush::$searchProvider; - } - - /** - * Returns the Backend for this request - * the backend has to be an IBackend implementation - * - * @access public - * @return object IBackend implementation - */ - static public function GetBackend() { - // if the backend is not yet loaded, load backend drivers and instantiate it - if (!isset(ZPush::$backend)) { - // Initialize our backend - $ourBackend = @constant('BACKEND_PROVIDER'); - - // if no backend provider is defined, try to include automatically - if ($ourBackend == false || $ourBackend == "") { - $loaded = false; - foreach (self::$autoloadBackendPreference as $autoloadBackend) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetBackend(): trying autoload backend '%s'", $autoloadBackend)); - $loaded = class_exists($autoloadBackend) || self::IncludeBackend($autoloadBackend); - if ($loaded) { - $ourBackend = $autoloadBackend; - break; - } - } - if (!$ourBackend || !$loaded) - throw new FatalMisconfigurationException("No Backend provider can not be loaded. Check your installation and configuration!"); - } - elseif (!class_exists($ourBackend)) - self::IncludeBackend($ourBackend); - - if (class_exists($ourBackend)) - ZPush::$backend = new $ourBackend(); - else - throw new FatalMisconfigurationException(sprintf("Backend provider '%s' can not be loaded. Check configuration!", $ourBackend)); - } - return ZPush::$backend; - } - - /** - * Returns additional folder objects which should be synchronized to the device - * - * @access public - * @return array - */ - static public function GetAdditionalSyncFolders() { - // TODO if there are any user based folders which should be synchronized, they have to be returned here as well!! - return self::$addSyncFolders; - } - - /** - * Returns additional folder objects which should be synchronized to the device - * - * @param string $folderid - * @param boolean $noDebug (opt) by default, debug message is shown - * - * @access public - * @return string - */ - static public function GetAdditionalSyncFolderStore($folderid, $noDebug = false) { - $val = (isset(self::$addSyncFolders[$folderid]->Store))? self::$addSyncFolders[$folderid]->Store : false; - if (!$noDebug) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetAdditionalSyncFolderStore('%s'): '%s'", $folderid, Utils::PrintAsString($val))); - return $val; - } - - /** - * Returns a SyncObject class name for a folder class - * - * @param string $folderclass - * - * @access public - * @return string - * @throws FatalNotImplementedException - */ - static public function getSyncObjectFromFolderClass($folderclass) { - if (!isset(self::$classes[$folderclass])) - throw new FatalNotImplementedException("Class '$folderclass' is not supported"); - - $class = self::$classes[$folderclass][self::CLASS_NAME]; - if (self::$classes[$folderclass][self::CLASS_REQUIRESPROTOCOLVERSION]) - return new $class(Request::GetProtocolVersion()); - else - return new $class(); - } - - /** - * Returns the default foldertype for a folder class - * - * @param string $folderclass folderclass sent by the mobile - * - * @access public - * @return string - */ - static public function getDefaultFolderTypeFromFolderClass($folderclass) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::getDefaultFolderTypeFromFolderClass('%s'): '%d'", $folderclass, self::$classes[$folderclass][self::CLASS_DEFAULTTYPE])); - return self::$classes[$folderclass][self::CLASS_DEFAULTTYPE]; - } - - /** - * Returns the folder class for a foldertype - * - * @param string $foldertype - * - * @access public - * @return string/false false if no class for this type is available - */ - static public function GetFolderClassFromFolderType($foldertype) { - $class = false; - foreach (self::$classes as $aClass => $cprops) { - if ($cprops[self::CLASS_DEFAULTTYPE] == $foldertype || in_array($foldertype, $cprops[self::CLASS_OTHERTYPES])) { - $class = $aClass; - break; - } - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::GetFolderClassFromFolderType('%s'): %s", $foldertype, Utils::PrintAsString($class))); - return $class; - } - - /** - * Prints the Z-Push legal header to STDOUT - * Using this breaks ActiveSync synchronization if wbxml is expected - * - * @param string $message (opt) message to be displayed - * @param string $additionalMessage (opt) additional message to be displayed - - * @access public - * @return - * - */ - static public function PrintZPushLegal($message = "", $additionalMessage = "") { - ZLog::Write(LOGLEVEL_DEBUG,"ZPush::PrintZPushLegal()"); - $zpush_version = @constant('ZPUSH_VERSION'); - - if ($message) - $message = "

". $message . "

"; - if ($additionalMessage) - $additionalMessage .= "
"; - - header("Content-type: text/html"); - print << -
- Z-Push ActiveSync -
- - -

Z-Push - Open Source ActiveSync

- Version $zpush_version
- $message $additionalMessage -

- More information about Z-Push can be found at:
- Z-Push homepage
- Z-Push download page
- Z-Push Bugtracker and Roadmap
-
- All modifications to this sourcecode must be published and returned to the community.
- Please see AGPLv3 License for details.
-
- - -END; - } - - /** - * Indicates the latest AS version supported by Z-Push - * - * @access public - * @return string - */ - static public function GetLatestSupportedASVersion() { - return end(self::$supportedASVersions); - } - - /** - * Indicates which is the highest AS version supported by the backend - * - * @access public - * @return string - * @throws FatalNotImplementedException if the backend returns an invalid version - */ - static public function GetSupportedASVersion() { - $version = self::GetBackend()->GetSupportedASVersion(); - if (!in_array($version, self::$supportedASVersions)) - throw new FatalNotImplementedException(sprintf("AS version '%s' reported by the backend is not supported", $version)); - - return $version; - } - - /** - * Returns AS server header - * - * @access public - * @return string - */ - static public function GetServerHeader() { - if (self::GetSupportedASVersion() == self::ASV_25) - return "MS-Server-ActiveSync: 6.5.7638.1"; - else - return "MS-Server-ActiveSync: ". self::GetSupportedASVersion(); - } - - /** - * Returns AS protocol versions which are supported - * - * @param boolean $valueOnly (opt) default: false (also returns the header name) - * - * @access public - * @return string - */ - static public function GetSupportedProtocolVersions($valueOnly = false) { - $versions = implode(',', array_slice(self::$supportedASVersions, 0, (array_search(self::GetSupportedASVersion(), self::$supportedASVersions)+1))); - ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedProtocolVersions(): " . $versions); - - if ($valueOnly === true) - return $versions; - - return "MS-ASProtocolVersions: " . $versions; - } - - /** - * Returns AS commands which are supported - * - * @access public - * @return string - */ - static public function GetSupportedCommands() { - $asCommands = array(); - // filter all non-activesync commands - foreach (self::$supportedCommands as $c=>$v) - if (!self::checkCommandOptions($c, self::NOACTIVESYNCCOMMAND) && - self::checkCommandOptions($c, self::GetSupportedASVersion())) - $asCommands[] = Utils::GetCommandFromCode($c); - - $commands = implode(',', $asCommands); - ZLog::Write(LOGLEVEL_DEBUG, "ZPush::GetSupportedCommands(): " . $commands); - return "MS-ASProtocolCommands: " . $commands; - } - - /** - * Loads and instantiates a request processor for a command - * - * @param int $commandCode - * - * @access public - * @return RequestProcessor sub-class - */ - static public function GetRequestHandlerForCommand($commandCode) { - if (!array_key_exists($commandCode, self::$requestHandler)) - throw new FatalNotImplementedException(sprintf("Command '%s' has no request handler or class", Utils::GetCommandFromCode($commandCode))); - - $class = self::$requestHandler[$commandCode]; - - if (class_exists($class)) - return new $class(); - else - throw new FatalNotImplementedException(sprintf("Request handler '%s' can not be loaded", $class)); - } - - /** - * Indicates if a commands requires authentication or not - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - static public function CommandNeedsAuthentication($commandCode) { - $stat = ! self::checkCommandOptions($commandCode, self::UNAUTHENTICATED); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsAuthentication(%d): %s", $commandCode, Utils::PrintAsString($stat))); - return $stat; - } - - /** - * Indicates if the Provisioning check has to be forced on these commands - * - * @param string $commandCode - - * @access public - * @return boolean - */ - static public function CommandNeedsProvisioning($commandCode) { - $stat = ! self::checkCommandOptions($commandCode, self::UNPROVISIONED); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsProvisioning(%s): %s", $commandCode, Utils::PrintAsString($stat))); - return $stat; - } - - /** - * Indicates if these commands expect plain text input instead of wbxml - * - * @param string $commandCode - * - * @access public - * @return boolean - */ - static public function CommandNeedsPlainInput($commandCode) { - $stat = self::checkCommandOptions($commandCode, self::PLAININPUT); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::CommandNeedsPlainInput(%d): %s", $commandCode, Utils::PrintAsString($stat))); - return $stat; - } - - /** - * Indicates if the comand to be executed operates on the hierarchy - * - * @param int $commandCode - - * @access public - * @return boolean - */ - static public function HierarchyCommand($commandCode) { - $stat = self::checkCommandOptions($commandCode, self::HIERARCHYCOMMAND); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPush::HierarchyCommand(%d): %s", $commandCode, Utils::PrintAsString($stat))); - return $stat; - } - - /** - * Checks access types of a command - * - * @param string $commandCode a commandCode - * @param string $option e.g. self::UNAUTHENTICATED - - * @access private - * @throws FatalNotImplementedException - * @return object StateMachine - */ - static private function checkCommandOptions($commandCode, $option) { - if ($commandCode === false) return false; - - if (!array_key_exists($commandCode, self::$supportedCommands)) - throw new FatalNotImplementedException(sprintf("Command '%s' is not supported", Utils::GetCommandFromCode($commandCode))); - - $capa = self::$supportedCommands[$commandCode]; - $defcapa = in_array($option, $capa, true); - - // if not looking for a default capability, check if the command is supported since a previous AS version - if (!$defcapa) { - $verkey = array_search($option, self::$supportedASVersions, true); - if ($verkey !== false && ($verkey >= array_search($capa[0], self::$supportedASVersions))) { - $defcapa = true; - } - } - - return $defcapa; - } - - /** - * End Response - * - * @access public - */ - public static function FinishResponse() { - $len = ob_get_length(); - if ($len !== false) { - if (!headers_sent()) { - header(sprintf("Content-Length: %s", $len)); - if ($len == 0) - header("Content-Type:"); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Flushing %d, headers already sent? %s", $len , headers_sent() ? "yes" : "no")); - if (!ob_end_flush()) - ZLog::Write(LOGLEVEL_ERROR, "Unable to flush buffer!?"); - } - } -} diff --git a/sources/lib/core/zpushdefs.php b/sources/lib/core/zpushdefs.php deleted file mode 100644 index 0e6ebbb..0000000 --- a/sources/lib/core/zpushdefs.php +++ /dev/null @@ -1,1075 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -const EN_TYPE = 1; -const EN_TAG = 2; -const EN_CONTENT = 3; -const EN_FLAGS = 4; -const EN_ATTRIBUTES = 5; -const EN_TYPE_STARTTAG = 1; -const EN_TYPE_ENDTAG = 2; -const EN_TYPE_CONTENT = 3; -const EN_FLAGS_CONTENT = 1; -const EN_FLAGS_ATTRIBUTES = 2; - -const SYNC_SYNCHRONIZE = "Synchronize"; -const SYNC_REPLIES = "Replies"; -const SYNC_ADD = "Add"; -const SYNC_MODIFY = "Modify"; -const SYNC_REMOVE = "Remove"; -const SYNC_FETCH = "Fetch"; -const SYNC_SYNCKEY = "SyncKey"; -const SYNC_CLIENTENTRYID = "ClientEntryId"; -const SYNC_SERVERENTRYID = "ServerEntryId"; -const SYNC_STATUS = "Status"; -const SYNC_FOLDER = "Folder"; -const SYNC_FOLDERTYPE = "FolderType"; -const SYNC_VERSION = "Version"; -const SYNC_FOLDERID = "FolderId"; -const SYNC_GETCHANGES = "GetChanges"; -const SYNC_MOREAVAILABLE = "MoreAvailable"; -const SYNC_MAXITEMS = "MaxItems"; -const SYNC_WINDOWSIZE = "WindowSize"; //MaxItems before z-push 2 -const SYNC_PERFORM = "Perform"; -const SYNC_OPTIONS = "Options"; -const SYNC_FILTERTYPE = "FilterType"; -const SYNC_TRUNCATION = "Truncation"; -const SYNC_RTFTRUNCATION = "RtfTruncation"; -const SYNC_CONFLICT = "Conflict"; -const SYNC_FOLDERS = "Folders"; -const SYNC_DATA = "Data"; -const SYNC_DELETESASMOVES = "DeletesAsMoves"; -const SYNC_NOTIFYGUID = "NotifyGUID"; -const SYNC_SUPPORTED = "Supported"; -const SYNC_SOFTDELETE = "SoftDelete"; -const SYNC_MIMESUPPORT = "MIMESupport"; -const SYNC_MIMETRUNCATION = "MIMETruncation"; -const SYNC_NEWMESSAGE = "NewMessage"; -const SYNC_WAIT = "Wait"; //12.1 and 14.0 -const SYNC_LIMIT = "Limit"; //12.1 and 14.0 -const SYNC_PARTIAL = "Partial"; //12.1 and 14.0 -const SYNC_CONVERSATIONMODE = "ConversationMode"; //14.0 -const SYNC_HEARTBEATINTERVAL = "HeartbeatInterval"; //14.0 - -// POOMCONTACTS -const SYNC_POOMCONTACTS_ANNIVERSARY = "POOMCONTACTS:Anniversary"; -const SYNC_POOMCONTACTS_ASSISTANTNAME = "POOMCONTACTS:AssistantName"; -const SYNC_POOMCONTACTS_ASSISTNAMEPHONENUMBER = "POOMCONTACTS:AssistnamePhoneNumber"; -const SYNC_POOMCONTACTS_BIRTHDAY = "POOMCONTACTS:Birthday"; -const SYNC_POOMCONTACTS_BODY = "POOMCONTACTS:Body"; -const SYNC_POOMCONTACTS_BODYSIZE = "POOMCONTACTS:BodySize"; -const SYNC_POOMCONTACTS_BODYTRUNCATED = "POOMCONTACTS:BodyTruncated"; -const SYNC_POOMCONTACTS_BUSINESS2PHONENUMBER = "POOMCONTACTS:Business2PhoneNumber"; -const SYNC_POOMCONTACTS_BUSINESSCITY = "POOMCONTACTS:BusinessCity"; -const SYNC_POOMCONTACTS_BUSINESSCOUNTRY = "POOMCONTACTS:BusinessCountry"; -const SYNC_POOMCONTACTS_BUSINESSPOSTALCODE = "POOMCONTACTS:BusinessPostalCode"; -const SYNC_POOMCONTACTS_BUSINESSSTATE = "POOMCONTACTS:BusinessState"; -const SYNC_POOMCONTACTS_BUSINESSSTREET = "POOMCONTACTS:BusinessStreet"; -const SYNC_POOMCONTACTS_BUSINESSFAXNUMBER = "POOMCONTACTS:BusinessFaxNumber"; -const SYNC_POOMCONTACTS_BUSINESSPHONENUMBER = "POOMCONTACTS:BusinessPhoneNumber"; -const SYNC_POOMCONTACTS_CARPHONENUMBER = "POOMCONTACTS:CarPhoneNumber"; -const SYNC_POOMCONTACTS_CATEGORIES = "POOMCONTACTS:Categories"; -const SYNC_POOMCONTACTS_CATEGORY = "POOMCONTACTS:Category"; -const SYNC_POOMCONTACTS_CHILDREN = "POOMCONTACTS:Children"; -const SYNC_POOMCONTACTS_CHILD = "POOMCONTACTS:Child"; -const SYNC_POOMCONTACTS_COMPANYNAME = "POOMCONTACTS:CompanyName"; -const SYNC_POOMCONTACTS_DEPARTMENT = "POOMCONTACTS:Department"; -const SYNC_POOMCONTACTS_EMAIL1ADDRESS = "POOMCONTACTS:Email1Address"; -const SYNC_POOMCONTACTS_EMAIL2ADDRESS = "POOMCONTACTS:Email2Address"; -const SYNC_POOMCONTACTS_EMAIL3ADDRESS = "POOMCONTACTS:Email3Address"; -const SYNC_POOMCONTACTS_FILEAS = "POOMCONTACTS:FileAs"; -const SYNC_POOMCONTACTS_FIRSTNAME = "POOMCONTACTS:FirstName"; -const SYNC_POOMCONTACTS_HOME2PHONENUMBER = "POOMCONTACTS:Home2PhoneNumber"; -const SYNC_POOMCONTACTS_HOMECITY = "POOMCONTACTS:HomeCity"; -const SYNC_POOMCONTACTS_HOMECOUNTRY = "POOMCONTACTS:HomeCountry"; -const SYNC_POOMCONTACTS_HOMEPOSTALCODE = "POOMCONTACTS:HomePostalCode"; -const SYNC_POOMCONTACTS_HOMESTATE = "POOMCONTACTS:HomeState"; -const SYNC_POOMCONTACTS_HOMESTREET = "POOMCONTACTS:HomeStreet"; -const SYNC_POOMCONTACTS_HOMEFAXNUMBER = "POOMCONTACTS:HomeFaxNumber"; -const SYNC_POOMCONTACTS_HOMEPHONENUMBER = "POOMCONTACTS:HomePhoneNumber"; -const SYNC_POOMCONTACTS_JOBTITLE = "POOMCONTACTS:JobTitle"; -const SYNC_POOMCONTACTS_LASTNAME = "POOMCONTACTS:LastName"; -const SYNC_POOMCONTACTS_MIDDLENAME = "POOMCONTACTS:MiddleName"; -const SYNC_POOMCONTACTS_MOBILEPHONENUMBER = "POOMCONTACTS:MobilePhoneNumber"; -const SYNC_POOMCONTACTS_OFFICELOCATION = "POOMCONTACTS:OfficeLocation"; -const SYNC_POOMCONTACTS_OTHERCITY = "POOMCONTACTS:OtherCity"; -const SYNC_POOMCONTACTS_OTHERCOUNTRY = "POOMCONTACTS:OtherCountry"; -const SYNC_POOMCONTACTS_OTHERPOSTALCODE = "POOMCONTACTS:OtherPostalCode"; -const SYNC_POOMCONTACTS_OTHERSTATE = "POOMCONTACTS:OtherState"; -const SYNC_POOMCONTACTS_OTHERSTREET = "POOMCONTACTS:OtherStreet"; -const SYNC_POOMCONTACTS_PAGERNUMBER = "POOMCONTACTS:PagerNumber"; -const SYNC_POOMCONTACTS_RADIOPHONENUMBER = "POOMCONTACTS:RadioPhoneNumber"; -const SYNC_POOMCONTACTS_SPOUSE = "POOMCONTACTS:Spouse"; -const SYNC_POOMCONTACTS_SUFFIX = "POOMCONTACTS:Suffix"; -const SYNC_POOMCONTACTS_TITLE = "POOMCONTACTS:Title"; -const SYNC_POOMCONTACTS_WEBPAGE = "POOMCONTACTS:WebPage"; -const SYNC_POOMCONTACTS_YOMICOMPANYNAME = "POOMCONTACTS:YomiCompanyName"; -const SYNC_POOMCONTACTS_YOMIFIRSTNAME = "POOMCONTACTS:YomiFirstName"; -const SYNC_POOMCONTACTS_YOMILASTNAME = "POOMCONTACTS:YomiLastName"; -const SYNC_POOMCONTACTS_RTF = "POOMCONTACTS:Rtf"; -const SYNC_POOMCONTACTS_PICTURE = "POOMCONTACTS:Picture"; -const SYNC_POOMCONTACTS_ALIAS = "POOMCONTACTS:Alias"; //14.0 -const SYNC_POOMCONTACTS_WEIGHEDRANK = "POOMCONTACTS:WeightedRank"; //14.0 - -// POOMMAIL -const SYNC_POOMMAIL_ATTACHMENT = "POOMMAIL:Attachment"; -const SYNC_POOMMAIL_ATTACHMENTS = "POOMMAIL:Attachments"; -const SYNC_POOMMAIL_ATTNAME = "POOMMAIL:AttName"; -const SYNC_POOMMAIL_ATTSIZE = "POOMMAIL:AttSize"; -const SYNC_POOMMAIL_ATTOID = "POOMMAIL:AttOid"; -const SYNC_POOMMAIL_ATTMETHOD = "POOMMAIL:AttMethod"; -const SYNC_POOMMAIL_ATTREMOVED = "POOMMAIL:AttRemoved"; -const SYNC_POOMMAIL_BODY = "POOMMAIL:Body"; -const SYNC_POOMMAIL_BODYSIZE = "POOMMAIL:BodySize"; -const SYNC_POOMMAIL_BODYTRUNCATED = "POOMMAIL:BodyTruncated"; -const SYNC_POOMMAIL_DATERECEIVED = "POOMMAIL:DateReceived"; -const SYNC_POOMMAIL_DISPLAYNAME = "POOMMAIL:DisplayName"; -const SYNC_POOMMAIL_DISPLAYTO = "POOMMAIL:DisplayTo"; -const SYNC_POOMMAIL_IMPORTANCE = "POOMMAIL:Importance"; -const SYNC_POOMMAIL_MESSAGECLASS = "POOMMAIL:MessageClass"; -const SYNC_POOMMAIL_SUBJECT = "POOMMAIL:Subject"; -const SYNC_POOMMAIL_READ = "POOMMAIL:Read"; -const SYNC_POOMMAIL_TO = "POOMMAIL:To"; -const SYNC_POOMMAIL_CC = "POOMMAIL:Cc"; -const SYNC_POOMMAIL_FROM = "POOMMAIL:From"; -const SYNC_POOMMAIL_REPLY_TO = "POOMMAIL:Reply-To"; -const SYNC_POOMMAIL_ALLDAYEVENT = "POOMMAIL:AllDayEvent"; -const SYNC_POOMMAIL_CATEGORIES = "POOMMAIL:Categories"; //not supported in 12.1 -const SYNC_POOMMAIL_CATEGORY = "POOMMAIL:Category"; //not supported in 12.1 -const SYNC_POOMMAIL_DTSTAMP = "POOMMAIL:DtStamp"; -const SYNC_POOMMAIL_ENDTIME = "POOMMAIL:EndTime"; -const SYNC_POOMMAIL_INSTANCETYPE = "POOMMAIL:InstanceType"; -const SYNC_POOMMAIL_BUSYSTATUS = "POOMMAIL:BusyStatus"; -const SYNC_POOMMAIL_LOCATION = "POOMMAIL:Location"; -const SYNC_POOMMAIL_MEETINGREQUEST = "POOMMAIL:MeetingRequest"; -const SYNC_POOMMAIL_ORGANIZER = "POOMMAIL:Organizer"; -const SYNC_POOMMAIL_RECURRENCEID = "POOMMAIL:RecurrenceId"; -const SYNC_POOMMAIL_REMINDER = "POOMMAIL:Reminder"; -const SYNC_POOMMAIL_RESPONSEREQUESTED = "POOMMAIL:ResponseRequested"; -const SYNC_POOMMAIL_RECURRENCES = "POOMMAIL:Recurrences"; -const SYNC_POOMMAIL_RECURRENCE = "POOMMAIL:Recurrence"; -const SYNC_POOMMAIL_TYPE = "POOMMAIL:Type"; -const SYNC_POOMMAIL_UNTIL = "POOMMAIL:Until"; -const SYNC_POOMMAIL_OCCURRENCES = "POOMMAIL:Occurrences"; -const SYNC_POOMMAIL_INTERVAL = "POOMMAIL:Interval"; -const SYNC_POOMMAIL_DAYOFWEEK = "POOMMAIL:DayOfWeek"; -const SYNC_POOMMAIL_DAYOFMONTH = "POOMMAIL:DayOfMonth"; -const SYNC_POOMMAIL_WEEKOFMONTH = "POOMMAIL:WeekOfMonth"; -const SYNC_POOMMAIL_MONTHOFYEAR = "POOMMAIL:MonthOfYear"; -const SYNC_POOMMAIL_STARTTIME = "POOMMAIL:StartTime"; -const SYNC_POOMMAIL_SENSITIVITY = "POOMMAIL:Sensitivity"; -const SYNC_POOMMAIL_TIMEZONE = "POOMMAIL:TimeZone"; -const SYNC_POOMMAIL_GLOBALOBJID = "POOMMAIL:GlobalObjId"; -const SYNC_POOMMAIL_THREADTOPIC = "POOMMAIL:ThreadTopic"; -const SYNC_POOMMAIL_MIMEDATA = "POOMMAIL:MIMEData"; -const SYNC_POOMMAIL_MIMETRUNCATED = "POOMMAIL:MIMETruncated"; -const SYNC_POOMMAIL_MIMESIZE = "POOMMAIL:MIMESize"; -const SYNC_POOMMAIL_INTERNETCPID = "POOMMAIL:InternetCPID"; -const SYNC_POOMMAIL_FLAG = "POOMMAIL:Flag"; //12.0, 12.1 and 14.0 -const SYNC_POOMMAIL_FLAGSTATUS = "POOMMAIL:FlagStatus"; //12.0, 12.1 and 14.0 -const SYNC_POOMMAIL_CONTENTCLASS = "POOMMAIL:ContentClass"; //12.0, 12.1 and 14.0 -const SYNC_POOMMAIL_FLAGTYPE = "POOMMAIL:FlagType"; //12.0, 12.1 and 14.0 -const SYNC_POOMMAIL_COMPLETETIME = "POOMMAIL:CompleteTime"; //14.0 -const SYNC_POOMMAIL_DISALLOWNEWTIMEPROPOSAL = "POOMMAIL:DisallowNewTimeProposal"; //14.0 - -// AIRNOTIFY -const SYNC_AIRNOTIFY_NOTIFY = "AirNotify:Notify"; -const SYNC_AIRNOTIFY_NOTIFICATION = "AirNotify:Notification"; -const SYNC_AIRNOTIFY_VERSION = "AirNotify:Version"; -const SYNC_AIRNOTIFY_LIFETIME = "AirNotify:Lifetime"; -const SYNC_AIRNOTIFY_DEVICEINFO = "AirNotify:DeviceInfo"; -const SYNC_AIRNOTIFY_ENABLE = "AirNotify:Enable"; -const SYNC_AIRNOTIFY_FOLDER = "AirNotify:Folder"; -const SYNC_AIRNOTIFY_SERVERENTRYID = "AirNotify:ServerEntryId"; -const SYNC_AIRNOTIFY_DEVICEADDRESS = "AirNotify:DeviceAddress"; -const SYNC_AIRNOTIFY_VALIDCARRIERPROFILES = "AirNotify:ValidCarrierProfiles"; -const SYNC_AIRNOTIFY_CARRIERPROFILE = "AirNotify:CarrierProfile"; -const SYNC_AIRNOTIFY_STATUS = "AirNotify:Status"; -const SYNC_AIRNOTIFY_REPLIES = "AirNotify:Replies"; -const SYNC_AIRNOTIFY_VERSION11 = "AirNotify:Version='1.1'"; -const SYNC_AIRNOTIFY_DEVICES = "AirNotify:Devices"; -const SYNC_AIRNOTIFY_DEVICE = "AirNotify:Device"; -const SYNC_AIRNOTIFY_ID = "AirNotify:Id"; -const SYNC_AIRNOTIFY_EXPIRY = "AirNotify:Expiry"; -const SYNC_AIRNOTIFY_NOTIFYGUID = "AirNotify:NotifyGUID"; - -// POOMCAL -const SYNC_POOMCAL_TIMEZONE = "POOMCAL:Timezone"; -const SYNC_POOMCAL_ALLDAYEVENT = "POOMCAL:AllDayEvent"; -const SYNC_POOMCAL_ATTENDEES = "POOMCAL:Attendees"; -const SYNC_POOMCAL_ATTENDEE = "POOMCAL:Attendee"; -const SYNC_POOMCAL_EMAIL = "POOMCAL:Email"; -const SYNC_POOMCAL_NAME = "POOMCAL:Name"; -const SYNC_POOMCAL_BODY = "POOMCAL:Body"; -const SYNC_POOMCAL_BODYTRUNCATED = "POOMCAL:BodyTruncated"; -const SYNC_POOMCAL_BUSYSTATUS = "POOMCAL:BusyStatus"; -const SYNC_POOMCAL_CATEGORIES = "POOMCAL:Categories"; -const SYNC_POOMCAL_CATEGORY = "POOMCAL:Category"; -const SYNC_POOMCAL_RTF = "POOMCAL:Rtf"; -const SYNC_POOMCAL_DTSTAMP = "POOMCAL:DtStamp"; -const SYNC_POOMCAL_ENDTIME = "POOMCAL:EndTime"; -const SYNC_POOMCAL_EXCEPTION = "POOMCAL:Exception"; -const SYNC_POOMCAL_EXCEPTIONS = "POOMCAL:Exceptions"; -const SYNC_POOMCAL_DELETED = "POOMCAL:Deleted"; -const SYNC_POOMCAL_EXCEPTIONSTARTTIME = "POOMCAL:ExceptionStartTime"; -const SYNC_POOMCAL_LOCATION = "POOMCAL:Location"; -const SYNC_POOMCAL_MEETINGSTATUS = "POOMCAL:MeetingStatus"; -const SYNC_POOMCAL_ORGANIZEREMAIL = "POOMCAL:OrganizerEmail"; -const SYNC_POOMCAL_ORGANIZERNAME = "POOMCAL:OrganizerName"; -const SYNC_POOMCAL_RECURRENCE = "POOMCAL:Recurrence"; -const SYNC_POOMCAL_TYPE = "POOMCAL:Type"; -const SYNC_POOMCAL_UNTIL = "POOMCAL:Until"; -const SYNC_POOMCAL_OCCURRENCES = "POOMCAL:Occurrences"; -const SYNC_POOMCAL_INTERVAL = "POOMCAL:Interval"; -const SYNC_POOMCAL_DAYOFWEEK = "POOMCAL:DayOfWeek"; -const SYNC_POOMCAL_DAYOFMONTH = "POOMCAL:DayOfMonth"; -const SYNC_POOMCAL_WEEKOFMONTH = "POOMCAL:WeekOfMonth"; -const SYNC_POOMCAL_MONTHOFYEAR = "POOMCAL:MonthOfYear"; -const SYNC_POOMCAL_REMINDER = "POOMCAL:Reminder"; -const SYNC_POOMCAL_SENSITIVITY = "POOMCAL:Sensitivity"; -const SYNC_POOMCAL_SUBJECT = "POOMCAL:Subject"; -const SYNC_POOMCAL_STARTTIME = "POOMCAL:StartTime"; -const SYNC_POOMCAL_UID = "POOMCAL:UID"; -const SYNC_POOMCAL_ATTENDEESTATUS = "POOMCAL:Attendee_Status"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTENDEETYPE = "POOMCAL:Attendee_Type"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTACHMENT = "POOMCAL:Attachment"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTACHMENTS = "POOMCAL:Attachments"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTNAME = "POOMCAL:AttName"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTSIZE = "POOMCAL:AttSize"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTOID = "POOMCAL:AttOid"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTMETHOD = "POOMCAL:AttMethod"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_ATTREMOVED = "POOMCAL:AttRemoved"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_DISPLAYNAME = "POOMCAL:DisplayName"; //12.0, 12.1 and 14.0 -const SYNC_POOMCAL_DISALLOWNEWTIMEPROPOSAL = "POOMCAL:DisallowNewTimeProposal"; //14.0 -const SYNC_POOMCAL_RESPONSEREQUESTED = "POOMCAL:ResponseRequested"; //14.0 -const SYNC_POOMCAL_APPOINTMENTREPLYTIME = "POOMCAL:AppointmentReplyTime"; //14.0 -const SYNC_POOMCAL_RESPONSETYPE = "POOMCAL:ResponseType"; //14.0 -const SYNC_POOMCAL_CALENDARTYPE = "POOMCAL:CalendarType"; //14.0 -const SYNC_POOMCAL_ISLEAPMONTH = "POOMCAL:IsLeapMonth"; //14.0 -const SYNC_POOMCAL_FIRSTDAYOFWEEK = "POOMCAL:FirstDayOfWeek"; //post 14.0 -const SYNC_POOMCAL_ONLINEMEETINGINTERNALLINK = "POOMCAL:OnlineMeetingInternalLink"; //post 14.0 -const SYNC_POOMCAL_ONLINEMEETINGEXTERNALLINK = "POOMCAL:OnlineMeetingExternalLink"; //post 14.0 - -// Move -const SYNC_MOVE_MOVES = "Move:Moves"; -const SYNC_MOVE_MOVE = "Move:Move"; -const SYNC_MOVE_SRCMSGID = "Move:SrcMsgId"; -const SYNC_MOVE_SRCFLDID = "Move:SrcFldId"; -const SYNC_MOVE_DSTFLDID = "Move:DstFldId"; -const SYNC_MOVE_RESPONSE = "Move:Response"; -const SYNC_MOVE_STATUS = "Move:Status"; -const SYNC_MOVE_DSTMSGID = "Move:DstMsgId"; - -// GetItemEstimate -const SYNC_GETITEMESTIMATE_GETITEMESTIMATE = "GetItemEstimate:GetItemEstimate"; -const SYNC_GETITEMESTIMATE_VERSION = "GetItemEstimate:Version"; -const SYNC_GETITEMESTIMATE_FOLDERS = "GetItemEstimate:Folders"; -const SYNC_GETITEMESTIMATE_FOLDER = "GetItemEstimate:Folder"; -const SYNC_GETITEMESTIMATE_FOLDERTYPE = "GetItemEstimate:FolderType"; -const SYNC_GETITEMESTIMATE_FOLDERID = "GetItemEstimate:FolderId"; -const SYNC_GETITEMESTIMATE_DATETIME = "GetItemEstimate:DateTime"; -const SYNC_GETITEMESTIMATE_ESTIMATE = "GetItemEstimate:Estimate"; -const SYNC_GETITEMESTIMATE_RESPONSE = "GetItemEstimate:Response"; -const SYNC_GETITEMESTIMATE_STATUS = "GetItemEstimate:Status"; - -// FolderHierarchy -const SYNC_FOLDERHIERARCHY_FOLDERS = "FolderHierarchy:Folders"; -const SYNC_FOLDERHIERARCHY_FOLDER = "FolderHierarchy:Folder"; -const SYNC_FOLDERHIERARCHY_DISPLAYNAME = "FolderHierarchy:DisplayName"; -const SYNC_FOLDERHIERARCHY_SERVERENTRYID = "FolderHierarchy:ServerEntryId"; -const SYNC_FOLDERHIERARCHY_PARENTID = "FolderHierarchy:ParentId"; -const SYNC_FOLDERHIERARCHY_TYPE = "FolderHierarchy:Type"; -const SYNC_FOLDERHIERARCHY_RESPONSE = "FolderHierarchy:Response"; -const SYNC_FOLDERHIERARCHY_STATUS = "FolderHierarchy:Status"; -const SYNC_FOLDERHIERARCHY_CONTENTCLASS = "FolderHierarchy:ContentClass"; -const SYNC_FOLDERHIERARCHY_CHANGES = "FolderHierarchy:Changes"; -const SYNC_FOLDERHIERARCHY_ADD = "FolderHierarchy:Add"; -const SYNC_FOLDERHIERARCHY_REMOVE = "FolderHierarchy:Remove"; -const SYNC_FOLDERHIERARCHY_UPDATE = "FolderHierarchy:Update"; -const SYNC_FOLDERHIERARCHY_SYNCKEY = "FolderHierarchy:SyncKey"; -const SYNC_FOLDERHIERARCHY_FOLDERCREATE = "FolderHierarchy:FolderCreate"; -const SYNC_FOLDERHIERARCHY_FOLDERDELETE = "FolderHierarchy:FolderDelete"; -const SYNC_FOLDERHIERARCHY_FOLDERUPDATE = "FolderHierarchy:FolderUpdate"; -const SYNC_FOLDERHIERARCHY_FOLDERSYNC = "FolderHierarchy:FolderSync"; -const SYNC_FOLDERHIERARCHY_COUNT = "FolderHierarchy:Count"; -const SYNC_FOLDERHIERARCHY_VERSION = "FolderHierarchy:Version"; -// only for internal use - never to be streamed to the mobile -const SYNC_FOLDERHIERARCHY_IGNORE_STORE = "FolderHierarchy:IgnoreStore"; - -// MeetingResponse -const SYNC_MEETINGRESPONSE_CALENDARID = "MeetingResponse:CalendarId"; -const SYNC_MEETINGRESPONSE_FOLDERID = "MeetingResponse:FolderId"; -const SYNC_MEETINGRESPONSE_MEETINGRESPONSE = "MeetingResponse:MeetingResponse"; -const SYNC_MEETINGRESPONSE_REQUESTID = "MeetingResponse:RequestId"; -const SYNC_MEETINGRESPONSE_REQUEST = "MeetingResponse:Request"; -const SYNC_MEETINGRESPONSE_RESULT = "MeetingResponse:Result"; -const SYNC_MEETINGRESPONSE_STATUS = "MeetingResponse:Status"; -const SYNC_MEETINGRESPONSE_USERRESPONSE = "MeetingResponse:UserResponse"; -const SYNC_MEETINGRESPONSE_VERSION = "MeetingResponse:Version"; -const SYNC_MEETINGRESPONSE_INSTANCEID = "MeetingResponse:InstanceId"; - -// POOMTASKS -const SYNC_POOMTASKS_BODY = "POOMTASKS:Body"; -const SYNC_POOMTASKS_BODYSIZE = "POOMTASKS:BodySize"; -const SYNC_POOMTASKS_BODYTRUNCATED = "POOMTASKS:BodyTruncated"; -const SYNC_POOMTASKS_CATEGORIES = "POOMTASKS:Categories"; -const SYNC_POOMTASKS_CATEGORY = "POOMTASKS:Category"; -const SYNC_POOMTASKS_COMPLETE = "POOMTASKS:Complete"; -const SYNC_POOMTASKS_DATECOMPLETED = "POOMTASKS:DateCompleted"; -const SYNC_POOMTASKS_DUEDATE = "POOMTASKS:DueDate"; -const SYNC_POOMTASKS_UTCDUEDATE = "POOMTASKS:UtcDueDate"; -const SYNC_POOMTASKS_IMPORTANCE = "POOMTASKS:Importance"; -const SYNC_POOMTASKS_RECURRENCE = "POOMTASKS:Recurrence"; -const SYNC_POOMTASKS_TYPE = "POOMTASKS:Type"; -const SYNC_POOMTASKS_START = "POOMTASKS:Start"; -const SYNC_POOMTASKS_UNTIL = "POOMTASKS:Until"; -const SYNC_POOMTASKS_OCCURRENCES = "POOMTASKS:Occurrences"; -const SYNC_POOMTASKS_INTERVAL = "POOMTASKS:Interval"; -const SYNC_POOMTASKS_DAYOFWEEK = "POOMTASKS:DayOfWeek"; -const SYNC_POOMTASKS_DAYOFMONTH = "POOMTASKS:DayOfMonth"; -const SYNC_POOMTASKS_WEEKOFMONTH = "POOMTASKS:WeekOfMonth"; -const SYNC_POOMTASKS_MONTHOFYEAR = "POOMTASKS:MonthOfYear"; -const SYNC_POOMTASKS_REGENERATE = "POOMTASKS:Regenerate"; -const SYNC_POOMTASKS_DEADOCCUR = "POOMTASKS:DeadOccur"; -const SYNC_POOMTASKS_REMINDERSET = "POOMTASKS:ReminderSet"; -const SYNC_POOMTASKS_REMINDERTIME = "POOMTASKS:ReminderTime"; -const SYNC_POOMTASKS_SENSITIVITY = "POOMTASKS:Sensitivity"; -const SYNC_POOMTASKS_STARTDATE = "POOMTASKS:StartDate"; -const SYNC_POOMTASKS_UTCSTARTDATE = "POOMTASKS:UtcStartDate"; -const SYNC_POOMTASKS_SUBJECT = "POOMTASKS:Subject"; -const SYNC_POOMTASKS_RTF = "POOMTASKS:Rtf"; -const SYNC_POOMTASKS_ORDINALDATE = "POOMTASKS:OrdinalDate"; //12.0, 12.1 and 14.0 -const SYNC_POOMTASKS_SUBORDINALDATE = "POOMTASKS:SubOrdinalDate"; //12.0, 12.1 and 14.0 -const SYNC_POOMTASKS_CALENDARTYPE = "POOMTASKS:CalendarType"; //14.0 -const SYNC_POOMTASKS_ISLEAPMONTH = "POOMTASKS:IsLeapMonth"; //14.0 -const SYNC_POOMTASKS_FIRSTDAYOFWEEK = "POOMTASKS:FirstDayOfWeek"; // post 14.0 - -// ResolveRecipients -const SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS = "ResolveRecipients:ResolveRecipients"; -const SYNC_RESOLVERECIPIENTS_RESPONSE = "ResolveRecipients:Response"; -const SYNC_RESOLVERECIPIENTS_STATUS = "ResolveRecipients:Status"; -const SYNC_RESOLVERECIPIENTS_TYPE = "ResolveRecipients:Type"; -const SYNC_RESOLVERECIPIENTS_RECIPIENT = "ResolveRecipients:Recipient"; -const SYNC_RESOLVERECIPIENTS_DISPLAYNAME = "ResolveRecipients:DisplayName"; -const SYNC_RESOLVERECIPIENTS_EMAILADDRESS = "ResolveRecipients:EmailAddress"; -const SYNC_RESOLVERECIPIENTS_CERTIFICATES = "ResolveRecipients:Certificates"; -const SYNC_RESOLVERECIPIENTS_CERTIFICATE = "ResolveRecipients:Certificate"; -const SYNC_RESOLVERECIPIENTS_MINICERTIFICATE = "ResolveRecipients:MiniCertificate"; -const SYNC_RESOLVERECIPIENTS_OPTIONS = "ResolveRecipients:Options"; -const SYNC_RESOLVERECIPIENTS_TO = "ResolveRecipients:To"; -const SYNC_RESOLVERECIPIENTS_CERTIFICATERETRIEVAL = "ResolveRecipients:CertificateRetrieval"; -const SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT = "ResolveRecipients:RecipientCount"; -const SYNC_RESOLVERECIPIENTS_MAXCERTIFICATES = "ResolveRecipients:MaxCertificates"; -const SYNC_RESOLVERECIPIENTS_MAXAMBIGUOUSRECIPIENTS = "ResolveRecipients:MaxAmbiguousRecipients"; -const SYNC_RESOLVERECIPIENTS_CERTIFICATECOUNT = "ResolveRecipients:CertificateCount"; -const SYNC_RESOLVERECIPIENTS_AVAILABILITY = "ResolveRecipients:Availability"; //14.0 -const SYNC_RESOLVERECIPIENTS_STARTTIME = "ResolveRecipients:StartTime"; //14.0 -const SYNC_RESOLVERECIPIENTS_ENDTIME = "ResolveRecipients:EndTime"; //14.0 -const SYNC_RESOLVERECIPIENTS_MERGEDFREEBUSY = "ResolveRecipients:MergedFreeBusy"; //14.0 -const SYNC_RESOLVERECIPIENTS_PICTURE = "ResolveRecipients:Picture"; //post 14.0 -const SYNC_RESOLVERECIPIENTS_MAXSIZE = "ResolveRecipients:MaxSize"; //post 14.0 -const SYNC_RESOLVERECIPIENTS_DATA = "ResolveRecipients:Data"; //post 14.0 -const SYNC_RESOLVERECIPIENTS_MAXPICTURES = "ResolveRecipients:MaxPictures"; //post 14.0 - -// ValidateCert -const SYNC_VALIDATECERT_VALIDATECERT = "ValidateCert:ValidateCert"; -const SYNC_VALIDATECERT_CERTIFICATES = "ValidateCert:Certificates"; -const SYNC_VALIDATECERT_CERTIFICATE = "ValidateCert:Certificate"; -const SYNC_VALIDATECERT_CERTIFICATECHAIN = "ValidateCert:CertificateChain"; -const SYNC_VALIDATECERT_CHECKCRL = "ValidateCert:CheckCRL"; -const SYNC_VALIDATECERT_STATUS = "ValidateCert:Status"; - -// POOMCONTACTS2 -const SYNC_POOMCONTACTS2_CUSTOMERID = "POOMCONTACTS2:CustomerId"; -const SYNC_POOMCONTACTS2_GOVERNMENTID = "POOMCONTACTS2:GovernmentId"; -const SYNC_POOMCONTACTS2_IMADDRESS = "POOMCONTACTS2:IMAddress"; -const SYNC_POOMCONTACTS2_IMADDRESS2 = "POOMCONTACTS2:IMAddress2"; -const SYNC_POOMCONTACTS2_IMADDRESS3 = "POOMCONTACTS2:IMAddress3"; -const SYNC_POOMCONTACTS2_MANAGERNAME = "POOMCONTACTS2:ManagerName"; -const SYNC_POOMCONTACTS2_COMPANYMAINPHONE = "POOMCONTACTS2:CompanyMainPhone"; -const SYNC_POOMCONTACTS2_ACCOUNTNAME = "POOMCONTACTS2:AccountName"; -const SYNC_POOMCONTACTS2_NICKNAME = "POOMCONTACTS2:NickName"; -const SYNC_POOMCONTACTS2_MMS = "POOMCONTACTS2:MMS"; - -// Ping -const SYNC_PING_PING = "Ping:Ping"; -const SYNC_PING_STATUS = "Ping:Status"; -const SYNC_PING_LIFETIME = "Ping:LifeTime"; -const SYNC_PING_FOLDERS = "Ping:Folders"; -const SYNC_PING_FOLDER = "Ping:Folder"; -const SYNC_PING_SERVERENTRYID = "Ping:ServerEntryId"; -const SYNC_PING_FOLDERTYPE = "Ping:FolderType"; -const SYNC_PING_MAXFOLDERS = "Ping:MaxFolders"; //missing in < z-push 2 -const SYNC_PING_VERSION = "Ping:Version"; //missing in < z-push 2 - -//Provision -const SYNC_PROVISION_PROVISION = "Provision:Provision"; -const SYNC_PROVISION_POLICIES = "Provision:Policies"; -const SYNC_PROVISION_POLICY = "Provision:Policy"; -const SYNC_PROVISION_POLICYTYPE = "Provision:PolicyType"; -const SYNC_PROVISION_POLICYKEY = "Provision:PolicyKey"; -const SYNC_PROVISION_DATA = "Provision:Data"; -const SYNC_PROVISION_STATUS = "Provision:Status"; -const SYNC_PROVISION_REMOTEWIPE = "Provision:RemoteWipe"; -const SYNC_PROVISION_EASPROVISIONDOC = "Provision:EASProvisionDoc"; -const SYNC_PROVISION_DEVPWENABLED = "Provision:DevicePasswordEnabled"; -const SYNC_PROVISION_ALPHANUMPWREQ = "Provision:AlphanumericDevicePasswordRequired"; -const SYNC_PROVISION_DEVENCENABLED = "Provision:DeviceEncryptionEnabled"; -const SYNC_PROVISION_REQSTORAGECARDENC = "Provision:RequireStorageCardEncryption"; -const SYNC_PROVISION_PWRECOVERYENABLED = "Provision:PasswordRecoveryEnabled"; -const SYNC_PROVISION_DOCBROWSEENABLED = "Provision:DocumentBrowseEnabled"; -const SYNC_PROVISION_ATTENABLED = "Provision:AttachmentsEnabled"; -const SYNC_PROVISION_MINDEVPWLENGTH = "Provision:MinDevicePasswordLength"; -const SYNC_PROVISION_MAXINACTTIMEDEVLOCK = "Provision:MaxInactivityTimeDeviceLock"; -const SYNC_PROVISION_MAXDEVPWFAILEDATTEMPTS = "Provision:MaxDevicePasswordFailedAttempts"; -const SYNC_PROVISION_MAXATTSIZE = "Provision:MaxAttachmentSize"; -const SYNC_PROVISION_ALLOWSIMPLEDEVPW = "Provision:AllowSimpleDevicePassword"; -const SYNC_PROVISION_DEVPWEXPIRATION = "Provision:DevicePasswordExpiration"; -const SYNC_PROVISION_DEVPWHISTORY = "Provision:DevicePasswordHistory"; -const SYNC_PROVISION_ALLOWSTORAGECARD = "Provision:AllowStorageCard"; -const SYNC_PROVISION_ALLOWCAM = "Provision:AllowCamera"; -const SYNC_PROVISION_REQDEVENC = "Provision:RequireDeviceEncryption"; -const SYNC_PROVISION_ALLOWUNSIGNEDAPPS = "Provision:AllowUnsignedApplications"; -const SYNC_PROVISION_ALLOWUNSIGNEDINSTALLATIONPACKAGES = "Provision:AllowUnsignedInstallationPackages"; -const SYNC_PROVISION_MINDEVPWCOMPLEXCHARS = "Provision:MinDevicePasswordComplexCharacters"; -const SYNC_PROVISION_ALLOWWIFI = "Provision:AllowWiFi"; -const SYNC_PROVISION_ALLOWTEXTMESSAGING = "Provision:AllowTextMessaging"; -const SYNC_PROVISION_ALLOWPOPIMAPEMAIL = "Provision:AllowPOPIMAPEmail"; -const SYNC_PROVISION_ALLOWBLUETOOTH = "Provision:AllowBluetooth"; -const SYNC_PROVISION_ALLOWIRDA = "Provision:AllowIrDA"; -const SYNC_PROVISION_REQMANUALSYNCWHENROAM = "Provision:RequireManualSyncWhenRoaming"; -const SYNC_PROVISION_ALLOWDESKTOPSYNC = "Provision:AllowDesktopSync"; -const SYNC_PROVISION_MAXCALAGEFILTER = "Provision:MaxCalendarAgeFilter"; -const SYNC_PROVISION_ALLOWHTMLEMAIL = "Provision:AllowHTMLEmail"; -const SYNC_PROVISION_MAXEMAILAGEFILTER = "Provision:MaxEmailAgeFilter"; -const SYNC_PROVISION_MAXEMAILBODYTRUNCSIZE = "Provision:MaxEmailBodyTruncationSize"; -const SYNC_PROVISION_MAXEMAILHTMLBODYTRUNCSIZE = "Provision:MaxEmailHTMLBodyTruncationSize"; -const SYNC_PROVISION_REQSIGNEDSMIMEMESSAGES = "Provision:RequireSignedSMIMEMessages"; -const SYNC_PROVISION_REQENCSMIMEMESSAGES = "Provision:RequireEncryptedSMIMEMessages"; -const SYNC_PROVISION_REQSIGNEDSMIMEALGORITHM = "Provision:RequireSignedSMIMEAlgorithm"; -const SYNC_PROVISION_REQENCSMIMEALGORITHM = "Provision:RequireEncryptionSMIMEAlgorithm"; -const SYNC_PROVISION_ALLOWSMIMEENCALGORITHNEG = "Provision:AllowSMIMEEncryptionAlgorithmNegotiation"; -const SYNC_PROVISION_ALLOWSMIMESOFTCERTS = "Provision:AllowSMIMESoftCerts"; -const SYNC_PROVISION_ALLOWBROWSER = "Provision:AllowBrowser"; -const SYNC_PROVISION_ALLOWCONSUMEREMAIL = "Provision:AllowConsumerEmail"; -const SYNC_PROVISION_ALLOWREMOTEDESKTOP = "Provision:AllowRemoteDesktop"; -const SYNC_PROVISION_ALLOWINTERNETSHARING = "Provision:AllowInternetSharing"; -const SYNC_PROVISION_UNAPPROVEDINROMAPPLIST = "Provision:UnapprovedInROMApplicationList"; -const SYNC_PROVISION_APPNAME = "Provision:ApplicationName"; -const SYNC_PROVISION_APPROVEDAPPLIST = "Provision:ApprovedApplicationList"; -const SYNC_PROVISION_HASH = "Provision:Hash"; - -//Search -const SYNC_SEARCH_SEARCH = "Search:Search"; -const SYNC_SEARCH_STORE = "Search:Store"; -const SYNC_SEARCH_NAME = "Search:Name"; -const SYNC_SEARCH_QUERY = "Search:Query"; -const SYNC_SEARCH_OPTIONS = "Search:Options"; -const SYNC_SEARCH_RANGE = "Search:Range"; -const SYNC_SEARCH_STATUS = "Search:Status"; -const SYNC_SEARCH_RESPONSE = "Search:Response"; -const SYNC_SEARCH_RESULT = "Search:Result"; -const SYNC_SEARCH_PROPERTIES = "Search:Properties"; -const SYNC_SEARCH_TOTAL = "Search:Total"; -const SYNC_SEARCH_EQUALTO = "Search:EqualTo"; -const SYNC_SEARCH_VALUE = "Search:Value"; -const SYNC_SEARCH_AND = "Search:And"; -const SYNC_SEARCH_OR = "Search:Or"; -const SYNC_SEARCH_FREETEXT = "Search:FreeText"; -const SYNC_SEARCH_DEEPTRAVERSAL = "Search:DeepTraversal"; -const SYNC_SEARCH_LONGID = "Search:LongId"; -const SYNC_SEARCH_REBUILDRESULTS = "Search:RebuildResults"; -const SYNC_SEARCH_LESSTHAN = "Search:LessThan"; -const SYNC_SEARCH_GREATERTHAN = "Search:GreaterThan"; -const SYNC_SEARCH_SCHEMA = "Search:Schema"; -const SYNC_SEARCH_SUPPORTED = "Search:Supported"; -const SYNC_SEARCH_USERNAME = "Search:UserName"; //12.1 and 14.0 -const SYNC_SEARCH_PASSWORD = "Search:Password"; //12.1 and 14.0 -const SYNC_SEARCH_CONVERSATIONID = "Search:ConversationId"; //14.0 -const SYNC_SEARCH_PICTURE = "Search:Picture"; //post 14.0 -const SYNC_SEARCH_MAXSIZE = "Search:MaxSize"; //post 14.0 -const SYNC_SEARCH_MAXPICTURES = "Search:MaxPictures"; //post 14.0 - -//GAL -const SYNC_GAL_DISPLAYNAME = "GAL:DisplayName"; -const SYNC_GAL_PHONE = "GAL:Phone"; -const SYNC_GAL_OFFICE = "GAL:Office"; -const SYNC_GAL_TITLE = "GAL:Title"; -const SYNC_GAL_COMPANY = "GAL:Company"; -const SYNC_GAL_ALIAS = "GAL:Alias"; -const SYNC_GAL_FIRSTNAME = "GAL:FirstName"; -const SYNC_GAL_LASTNAME = "GAL:LastName"; -const SYNC_GAL_HOMEPHONE = "GAL:HomePhone"; -const SYNC_GAL_MOBILEPHONE = "GAL:MobilePhone"; -const SYNC_GAL_EMAILADDRESS = "GAL:EmailAddress"; -const SYNC_GAL_PICTURE = "GAL:Picture"; //post 14.0 -const SYNC_GAL_MAXSIZE = "GAL:Status"; //post 14.0 -const SYNC_GAL_DATA = "GAL:Data"; //post 14.0 - -//AirSyncBase //12.0, 12.1 and 14.0 -const SYNC_AIRSYNCBASE_BODYPREFERENCE = "AirSyncBase:BodyPreference"; -const SYNC_AIRSYNCBASE_TYPE = "AirSyncBase:Type"; -const SYNC_AIRSYNCBASE_TRUNCATIONSIZE = "AirSyncBase:TruncationSize"; -const SYNC_AIRSYNCBASE_ALLORNONE = "AirSyncBase:AllOrNone"; -const SYNC_AIRSYNCBASE_BODY = "AirSyncBase:Body"; -const SYNC_AIRSYNCBASE_DATA = "AirSyncBase:Data"; -const SYNC_AIRSYNCBASE_ESTIMATEDDATASIZE = "AirSyncBase:EstimatedDataSize"; -const SYNC_AIRSYNCBASE_TRUNCATED = "AirSyncBase:Truncated"; -const SYNC_AIRSYNCBASE_ATTACHMENTS = "AirSyncBase:Attachments"; -const SYNC_AIRSYNCBASE_ATTACHMENT = "AirSyncBase:Attachment"; -const SYNC_AIRSYNCBASE_DISPLAYNAME = "AirSyncBase:DisplayName"; -const SYNC_AIRSYNCBASE_FILEREFERENCE = "AirSyncBase:FileReference"; -const SYNC_AIRSYNCBASE_METHOD = "AirSyncBase:Method"; -const SYNC_AIRSYNCBASE_CONTENTID = "AirSyncBase:ContentId"; -const SYNC_AIRSYNCBASE_CONTENTLOCATION = "AirSyncBase:ContentLocation"; //not used -const SYNC_AIRSYNCBASE_ISINLINE = "AirSyncBase:IsInline"; -const SYNC_AIRSYNCBASE_NATIVEBODYTYPE = "AirSyncBase:NativeBodyType"; -const SYNC_AIRSYNCBASE_CONTENTTYPE = "AirSyncBase:ContentType"; -const SYNC_AIRSYNCBASE_PREVIEW = "AirSyncBase:Preview"; //14.0 -const SYNC_AIRSYNCBASE_BODYPARTPREFERENCE = "AirSyncBase:BodyPartPreference"; //post 14.0 -const SYNC_AIRSYNCBASE_BODYPART = "AirSyncBase:BodyPart"; //post 14.0 -const SYNC_AIRSYNCBASE_STATUS = "AirSyncBase:Status"; //post 14.0 - -//Settings //12.0, 12.1 and 14.0 -const SYNC_SETTINGS_SETTINGS = "Settings:Settings"; -const SYNC_SETTINGS_STATUS = "Settings:Status"; -const SYNC_SETTINGS_GET = "Settings:Get"; -const SYNC_SETTINGS_SET = "Settings:Set"; -const SYNC_SETTINGS_OOF = "Settings:Oof"; -const SYNC_SETTINGS_OOFSTATE = "Settings:OofState"; -const SYNC_SETTINGS_STARTTIME = "Settings:StartTime"; -const SYNC_SETTINGS_ENDTIME = "Settings:EndTime"; -const SYNC_SETTINGS_OOFMESSAGE = "Settings:OofMessage"; -const SYNC_SETTINGS_APPLIESTOINTERVAL = "Settings:AppliesToInternal"; -const SYNC_SETTINGS_APPLIESTOEXTERNALKNOWN = "Settings:AppliesToExternalKnown"; -const SYNC_SETTINGS_APPLIESTOEXTERNALUNKNOWN = "Settings:AppliesToExternalUnknown"; -const SYNC_SETTINGS_ENABLED = "Settings:Enabled"; -const SYNC_SETTINGS_REPLYMESSAGE = "Settings:ReplyMessage"; -const SYNC_SETTINGS_BODYTYPE = "Settings:BodyType"; -const SYNC_SETTINGS_DEVICEPW = "Settings:DevicePassword"; -const SYNC_SETTINGS_PW = "Settings:Password"; -const SYNC_SETTINGS_DEVICEINFORMATION = "Settings:DeviceInformaton"; -const SYNC_SETTINGS_MODEL = "Settings:Model"; -const SYNC_SETTINGS_IMEI = "Settings:IMEI"; -const SYNC_SETTINGS_FRIENDLYNAME = "Settings:FriendlyName"; -const SYNC_SETTINGS_OS = "Settings:OS"; -const SYNC_SETTINGS_OSLANGUAGE = "Settings:OSLanguage"; -const SYNC_SETTINGS_PHONENUMBER = "Settings:PhoneNumber"; -const SYNC_SETTINGS_USERINFORMATION = "Settings:UserInformation"; -const SYNC_SETTINGS_EMAILADDRESSES = "Settings:EmailAddresses"; -const SYNC_SETTINGS_SMPTADDRESS = "Settings:SmtpAddress"; -const SYNC_SETTINGS_USERAGENT = "Settings:UserAgent"; //12.1 and 14.0 -const SYNC_SETTINGS_ENABLEOUTBOUNDSMS = "Settings:EnableOutboundSMS"; //14.0 -const SYNC_SETTINGS_MOBILEOPERATOR = "Settings:MobileOperator"; //14.0 -const SYNC_SETTINGS_PRIMARYSMTPADDRESS = "Settings:PrimarySmtpAddress"; -const SYNC_SETTINGS_ACCOUNTS = "Settings:Accounts"; -const SYNC_SETTINGS_ACCOUNT = "Settings:Account"; -const SYNC_SETTINGS_ACCOUNTID = "Settings:AccountId"; -const SYNC_SETTINGS_ACCOUNTNAME = "Settings:AccountName"; -const SYNC_SETTINGS_USERDISPLAYNAME = "Settings:UserDisplayName"; //12.1 and 14.0 -const SYNC_SETTINGS_SENDDISABLED = "Settings:SendDisabled"; //14.0 -const SYNC_SETTINGS_IHSMANAGEMENTINFORMATION = "Settings:ihsManagementInformation"; //14.0 -// only for internal use - never to be streamed to the mobile -const SYNC_SETTINGS_PROP_STATUS = "Settings:PropertyStatus"; - -//DocumentLibrary //12.0, 12.1 and 14.0 -const SYNC_DOCUMENTLIBRARY_LINKID = "DocumentLibrary:LinkId"; -const SYNC_DOCUMENTLIBRARY_DISPLAYNAME = "DocumentLibrary:DisplayName"; -const SYNC_DOCUMENTLIBRARY_ISFOLDER = "DocumentLibrary:IsFolder"; -const SYNC_DOCUMENTLIBRARY_CREATIONDATE = "DocumentLibrary:CreationDate"; -const SYNC_DOCUMENTLIBRARY_LASTMODIFIEDDATE = "DocumentLibrary:LastModifiedDate"; -const SYNC_DOCUMENTLIBRARY_ISHIDDEN = "DocumentLibrary:IsHidden"; -const SYNC_DOCUMENTLIBRARY_CONTENTLENGTH = "DocumentLibrary:ContentLength"; -const SYNC_DOCUMENTLIBRARY_CONTENTTYPE = "DocumentLibrary:ContentType"; - -//ItemOperations //12.0, 12.1 and 14.0 -const SYNC_ITEMOPERATIONS_ITEMOPERATIONS = "ItemOperations:ItemOperations"; -const SYNC_ITEMOPERATIONS_FETCH = "ItemOperations:Fetch"; -const SYNC_ITEMOPERATIONS_STORE = "ItemOperations:Store"; -const SYNC_ITEMOPERATIONS_OPTIONS = "ItemOperations:Options"; -const SYNC_ITEMOPERATIONS_RANGE = "ItemOperations:Range"; -const SYNC_ITEMOPERATIONS_TOTAL = "ItemOperations:Total"; -const SYNC_ITEMOPERATIONS_PROPERTIES = "ItemOperations:Properties"; -const SYNC_ITEMOPERATIONS_DATA = "ItemOperations:Data"; -const SYNC_ITEMOPERATIONS_STATUS = "ItemOperations:Status"; -const SYNC_ITEMOPERATIONS_RESPONSE = "ItemOperations:Response"; -const SYNC_ITEMOPERATIONS_VERSIONS = "ItemOperations:Version"; -const SYNC_ITEMOPERATIONS_SCHEMA = "ItemOperations:Schema"; -const SYNC_ITEMOPERATIONS_PART = "ItemOperations:Part"; -const SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS = "ItemOperations:EmptyFolderContents"; -const SYNC_ITEMOPERATIONS_DELETESUBFOLDERS = "ItemOperations:DeleteSubFolders"; -const SYNC_ITEMOPERATIONS_USERNAME = "ItemOperations:UserName"; //12.1 and 14.0 -const SYNC_ITEMOPERATIONS_PASSWORD = "ItemOperations:Password"; //12.1 and 14.0 -const SYNC_ITEMOPERATIONS_MOVE = "ItemOperations:Move"; //14.0 -const SYNC_ITEMOPERATIONS_DSTFLDID = "ItemOperations:DstFldId"; //14.0 -const SYNC_ITEMOPERATIONS_CONVERSATIONID = "ItemOperations:ConversationId"; //14.0 -const SYNC_ITEMOPERATIONS_MOVEALWAYS = "ItemOperations:MoveAlways"; //14.0 - -//ComposeMail //14.0 -const SYNC_COMPOSEMAIL_SENDMAIL = "ComposeMail:SendMail"; -const SYNC_COMPOSEMAIL_SMARTFORWARD = "ComposeMail:SmartForward"; -const SYNC_COMPOSEMAIL_SMARTREPLY = "ComposeMail:SmartReply"; -const SYNC_COMPOSEMAIL_SAVEINSENTITEMS = "ComposeMail:SaveInSentItems"; -const SYNC_COMPOSEMAIL_REPLACEMIME = "ComposeMail:ReplaceMime"; -const SYNC_COMPOSEMAIL_TYPE = "ComposeMail:Type"; -const SYNC_COMPOSEMAIL_SOURCE = "ComposeMail:Source"; -const SYNC_COMPOSEMAIL_FOLDERID = "ComposeMail:FolderId"; -const SYNC_COMPOSEMAIL_ITEMID = "ComposeMail:ItemId"; -const SYNC_COMPOSEMAIL_LONGID = "ComposeMail:LongId"; -const SYNC_COMPOSEMAIL_INSTANCEID = "ComposeMail:InstanceId"; -const SYNC_COMPOSEMAIL_MIME = "ComposeMail:MIME"; -const SYNC_COMPOSEMAIL_CLIENTID = "ComposeMail:ClientId"; -const SYNC_COMPOSEMAIL_STATUS = "ComposeMail:Status"; -const SYNC_COMPOSEMAIL_ACCOUNTID = "ComposeMail:AccountId"; -// only for internal use - never to be streamed to the mobile -const SYNC_COMPOSEMAIL_REPLYFLAG = "ComposeMail:ReplyFlag"; -const SYNC_COMPOSEMAIL_FORWARDFLAG = "ComposeMail:ForwardFlag"; - -//POOMMAIL2 //14.0 -const SYNC_POOMMAIL2_UMCALLERID = "POOMMAIL2:UmCallerId"; -const SYNC_POOMMAIL2_UMUSERNOTES = "POOMMAIL2:UmUserNotes"; -const SYNC_POOMMAIL2_UMATTDURATION = "POOMMAIL2:UmAttDuration"; -const SYNC_POOMMAIL2_UMATTORDER = "POOMMAIL2:UmAttOrder"; -const SYNC_POOMMAIL2_CONVERSATIONID = "POOMMAIL2:ConversationId"; -const SYNC_POOMMAIL2_CONVERSATIONINDEX = "POOMMAIL2:ConversationIndex"; -const SYNC_POOMMAIL2_LASTVERBEXECUTED = "POOMMAIL2:LastVerbExecuted"; -const SYNC_POOMMAIL2_LASTVERBEXECUTIONTIME = "POOMMAIL2:LastVerbExecutionTime"; -const SYNC_POOMMAIL2_RECEIVEDASBCC = "POOMMAIL2:ReceivedAsBcc"; -const SYNC_POOMMAIL2_SENDER = "POOMMAIL2:Sender"; -const SYNC_POOMMAIL2_CALENDARTYPE = "POOMMAIL2:CalendarType"; -const SYNC_POOMMAIL2_ISLEAPMONTH = "POOMMAIL2:IsLeapMonth"; -const SYNC_POOMMAIL2_ACCOUNTID = "POOMMAIL2:AccountId"; -const SYNC_POOMMAIL2_FIRSTDAYOFWEEK = "POOMMAIL2:FirstDayOfWeek"; -const SYNC_POOMMAIL2_MEETINGMESSAGETYPE = "POOMMAIL2:MeetingMessageType"; - -//Notes //14.0 -const SYNC_NOTES_SUBJECT = "Notes:Subject"; -const SYNC_NOTES_MESSAGECLASS = "Notes:MessageClass"; -const SYNC_NOTES_LASTMODIFIEDDATE = "Notes:LastModifiedDate"; -const SYNC_NOTES_CATEGORIES = "Notes:Categories"; -const SYNC_NOTES_CATEGORY = "Notes:Category"; - -//RightsManagement //post 14.0 -const SYNC_RIGHTSMANAGEMENT_SUPPORT = "RightsManagement:RightsManagementSupport"; -const SYNC_RIGHTSMANAGEMENT_TEMPLATES = "RightsManagement:RightsManagementTemplates"; -const SYNC_RIGHTSMANAGEMENT_TEMPLATE = "RightsManagement:RightsManagementTemplate"; -const SYNC_RIGHTSMANAGEMENT_LICENSE = "RightsManagement:RightsManagementLicense"; -const SYNC_RIGHTSMANAGEMENT_EDITALLOWED = "RightsManagement:EditAllowed"; -const SYNC_RIGHTSMANAGEMENT_REPLYALLOWED = "RightsManagement:ReplyAllowed"; -const SYNC_RIGHTSMANAGEMENT_REPLYALLALLOWED = "RightsManagement:ReplyAllAllowed"; -const SYNC_RIGHTSMANAGEMENT_FORWARDALLOWED = "RightsManagement:ForwardAllowed"; -const SYNC_RIGHTSMANAGEMENT_MODIFYRECIPIENTSALLOWED = "RightsManagement:ModifyRecipientsAllowed"; -const SYNC_RIGHTSMANAGEMENT_EXTRACTALLOWED = "RightsManagement:ExtractAllowed"; -const SYNC_RIGHTSMANAGEMENT_PRINTALLOWED = "RightsManagement:PrintAllowed"; -const SYNC_RIGHTSMANAGEMENT_EXPORTALLOWED = "RightsManagement:ExportAllowed"; -const SYNC_RIGHTSMANAGEMENT_PROGRAMMATICACCESSALLOWED = "RightsManagement:ProgrammaticAccessAllowed"; -const SYNC_RIGHTSMANAGEMENT_RMOWNER = "RightsManagement:RMOwner"; -const SYNC_RIGHTSMANAGEMENT_CONTENTEXPIRYDATE = "RightsManagement:ContentExpiryDate"; -const SYNC_RIGHTSMANAGEMENT_TEMPLATEID = "RightsManagement:TemplateID"; -const SYNC_RIGHTSMANAGEMENT_TEMPLATENAME = "RightsManagement:TemplateName"; -const SYNC_RIGHTSMANAGEMENT_TEMPLATEDESCRIPTION = "RightsManagement:TemplateDescription"; -const SYNC_RIGHTSMANAGEMENT_CONTENTOWNER = "RightsManagement:ContentOwner"; -const SYNC_RIGHTSMANAGEMENT_REMOVERIGHTSMGNTDIST = "RightsManagement:RemoveRightsManagementDistribution"; - -// Other constants -const SYNC_FOLDER_TYPE_OTHER = 1; -const SYNC_FOLDER_TYPE_INBOX = 2; -const SYNC_FOLDER_TYPE_DRAFTS = 3; -const SYNC_FOLDER_TYPE_WASTEBASKET = 4; -const SYNC_FOLDER_TYPE_SENTMAIL = 5; -const SYNC_FOLDER_TYPE_OUTBOX = 6; -const SYNC_FOLDER_TYPE_TASK = 7; -const SYNC_FOLDER_TYPE_APPOINTMENT = 8; -const SYNC_FOLDER_TYPE_CONTACT = 9; -const SYNC_FOLDER_TYPE_NOTE = 10; -const SYNC_FOLDER_TYPE_JOURNAL = 11; -const SYNC_FOLDER_TYPE_USER_MAIL = 12; -const SYNC_FOLDER_TYPE_USER_APPOINTMENT = 13; -const SYNC_FOLDER_TYPE_USER_CONTACT = 14; -const SYNC_FOLDER_TYPE_USER_TASK = 15; -const SYNC_FOLDER_TYPE_USER_JOURNAL = 16; -const SYNC_FOLDER_TYPE_USER_NOTE = 17; -const SYNC_FOLDER_TYPE_UNKNOWN = 18; -const SYNC_FOLDER_TYPE_RECIPIENT_CACHE = 19; -const SYNC_FOLDER_TYPE_DUMMY = 999999; - -const SYNC_CONFLICT_OVERWRITE_SERVER = 0; -const SYNC_CONFLICT_OVERWRITE_PIM = 1; - -const SYNC_FILTERTYPE_ALL = 0; -const SYNC_FILTERTYPE_1DAY = 1; -const SYNC_FILTERTYPE_3DAYS = 2; -const SYNC_FILTERTYPE_1WEEK = 3; -const SYNC_FILTERTYPE_2WEEKS = 4; -const SYNC_FILTERTYPE_1MONTH = 5; -const SYNC_FILTERTYPE_3MONTHS = 6; -const SYNC_FILTERTYPE_6MONTHS = 7; -const SYNC_FILTERTYPE_INCOMPLETETASKS = 8; - -const SYNC_TRUNCATION_HEADERS = 0; -const SYNC_TRUNCATION_512B = 1; -const SYNC_TRUNCATION_1K = 2; -const SYNC_TRUNCATION_2K = 3; -const SYNC_TRUNCATION_5K = 4; -const SYNC_TRUNCATION_10K = 5; -const SYNC_TRUNCATION_20K = 6; -const SYNC_TRUNCATION_50K = 7; -const SYNC_TRUNCATION_100K = 8; -const SYNC_TRUNCATION_ALL = 9; - -const SYNC_PROVISION_STATUS_SUCCESS = 1; -const SYNC_PROVISION_STATUS_PROTERROR = 2; -const SYNC_PROVISION_STATUS_SERVERERROR = 3; -const SYNC_PROVISION_STATUS_DEVEXTMANAGED = 4; - -const SYNC_PROVISION_POLICYSTATUS_SUCCESS = 1; -const SYNC_PROVISION_POLICYSTATUS_NOPOLICY = 2; -const SYNC_PROVISION_POLICYSTATUS_UNKNOWNVALUE = 3; -const SYNC_PROVISION_POLICYSTATUS_CORRUPTED = 4; -const SYNC_PROVISION_POLICYSTATUS_POLKEYMISM = 5; - -const SYNC_PROVISION_RWSTATUS_NA = 0; -const SYNC_PROVISION_RWSTATUS_OK = 1; -const SYNC_PROVISION_RWSTATUS_PENDING = 2; -const SYNC_PROVISION_RWSTATUS_REQUESTED = 4; -const SYNC_PROVISION_RWSTATUS_WIPED = 8; - -const SYNC_STATUS_SUCCESS = 1; -const SYNC_STATUS_INVALIDSYNCKEY = 3; -const SYNC_STATUS_PROTOCOLLERROR = 4; -const SYNC_STATUS_SERVERERROR = 5; -const SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR = 6; -const SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT = 7; -const SYNC_STATUS_OBJECTNOTFOUND = 8; -const SYNC_STATUS_SYNCCANNOTBECOMPLETED = 9; -const SYNC_STATUS_FOLDERHIERARCHYCHANGED = 12; -const SYNC_STATUS_SYNCREQUESTINCOMPLETE = 13; -const SYNC_STATUS_INVALIDWAITORHBVALUE = 14; -const SYNC_STATUS_SYNCREQUESTINVALID = 15; -const SYNC_STATUS_RETRY = 16; - -const SYNC_FSSTATUS_SUCCESS = 1; -const SYNC_FSSTATUS_FOLDEREXISTS = 2; -const SYNC_FSSTATUS_SYSTEMFOLDER = 3; -const SYNC_FSSTATUS_FOLDERDOESNOTEXIST = 4; -const SYNC_FSSTATUS_PARENTNOTFOUND = 5; -const SYNC_FSSTATUS_SERVERERROR = 6; -const SYNC_FSSTATUS_REQUESTTIMEOUT = 8; -const SYNC_FSSTATUS_SYNCKEYERROR = 9; -const SYNC_FSSTATUS_MAILFORMEDREQ = 10; -const SYNC_FSSTATUS_UNKNOWNERROR = 11; -const SYNC_FSSTATUS_CODEUNKNOWN = 12; - -const SYNC_GETITEMESTSTATUS_SUCCESS = 1; -const SYNC_GETITEMESTSTATUS_COLLECTIONINVALID = 2; -const SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED = 3; -const SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID = 4; - -const SYNC_ITEMOPERATIONSSTATUS_SUCCESS = 1; -const SYNC_ITEMOPERATIONSSTATUS_PROTERROR = 2; -const SYNC_ITEMOPERATIONSSTATUS_SERVERERROR = 3; -const SYNC_ITEMOPERATIONSSTATUS_DL_BADURI = 4; -const SYNC_ITEMOPERATIONSSTATUS_DL_ACCESSDENIED = 5; -const SYNC_ITEMOPERATIONSSTATUS_DL_NOTFOUND = 6; -const SYNC_ITEMOPERATIONSSTATUS_DL_CONNFAILED = 7; -const SYNC_ITEMOPERATIONSSTATUS_DL_BYTERANGEINVALID = 8; -const SYNC_ITEMOPERATIONSSTATUS_DL_STOREUNKNOWN = 9; -const SYNC_ITEMOPERATIONSSTATUS_DL_EMPTYFILE = 10; -const SYNC_ITEMOPERATIONSSTATUS_DL_TOOLARGE = 11; -const SYNC_ITEMOPERATIONSSTATUS_DL_IOFAILURE = 12; -const SYNC_ITEMOPERATIONSSTATUS_CONVERSIONFAILED = 14; -const SYNC_ITEMOPERATIONSSTATUS_INVALIDATT = 15; -const SYNC_ITEMOPERATIONSSTATUS_BLOCKED = 16; -const SYNC_ITEMOPERATIONSSTATUS_EMPTYFOLDER = 17; -const SYNC_ITEMOPERATIONSSTATUS_CREDSREQUIRED = 18; -const SYNC_ITEMOPERATIONSSTATUS_PROTOCOLERROR = 155; -const SYNC_ITEMOPERATIONSSTATUS_UNSUPPORTEDACTION = 156; - -const SYNC_MEETRESPSTATUS_SUCCESS = 1; -const SYNC_MEETRESPSTATUS_INVALIDMEETREQ = 2; -const SYNC_MEETRESPSTATUS_MAILBOXERROR = 3; -const SYNC_MEETRESPSTATUS_SERVERERROR = 4; - -const SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID = 1; -const SYNC_MOVEITEMSSTATUS_INVALIDDESTID = 2; -const SYNC_MOVEITEMSSTATUS_SUCCESS = 3; -const SYNC_MOVEITEMSSTATUS_SAMESOURCEANDDEST = 4; -const SYNC_MOVEITEMSSTATUS_CANNOTMOVE = 5; -const SYNC_MOVEITEMSSTATUS_SOURCEORDESTLOCKED = 7; - -const SYNC_PINGSTATUS_HBEXPIRED = 1; -const SYNC_PINGSTATUS_CHANGES = 2; -const SYNC_PINGSTATUS_FAILINGPARAMS = 3; -const SYNC_PINGSTATUS_SYNTAXERROR = 4; -const SYNC_PINGSTATUS_HBOUTOFRANGE = 5; -const SYNC_PINGSTATUS_TOOMUCHFOLDERS = 6; -const SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED = 7; -const SYNC_PINGSTATUS_SERVERERROR = 8; - -const SYNC_RESOLVERECIPSSTATUS_SUCCESS = 1; -const SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR = 5; -const SYNC_RESOLVERECIPSSTATUS_SERVERERROR = 6; -const SYNC_RESOLVERECIPSSTATUS_RESPONSE_SUCCESS = 1; -const SYNC_RESOLVERECIPSSTATUS_RESPONSE_AMBRECIP = 2; -const SYNC_RESOLVERECIPSSTATUS_RESPONSE_AMBRECIPPARTIAL = 3; -const SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP = 4; -const SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_SUCCESS = 1; -const SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_NOVALIDCERT = 7; -const SYNC_RESOLVERECIPSSTATUS_CERTIFICATES_CERTLIMIT = 8; -const SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_SUCCESS = 1; -const SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_MORETHAN100 = 160; -const SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_MORETHAN20 = 161; -const SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_REISSUE = 162; -const SYNC_RESOLVERECIPSSTATUS_AVAILABILITY_FAILED = 163; -const SYNC_RESOLVERECIPSSTATUS_PICTURE_SUCCESS = 1; -const SYNC_RESOLVERECIPSSTATUS_PICTURE_NOFOTO = 173; -const SYNC_RESOLVERECIPSSTATUS_PICTURE_MAXSIZEEXCEEDED = 174; -const SYNC_RESOLVERECIPSSTATUS_PICTURE_MAXPICTURESEXCEEDED = 175; - -const SYNC_SEARCHSTATUS_SUCCESS = 1; -const SYNC_SEARCHSTATUS_SERVERERROR = 3; -const SYNC_SEARCHSTATUS_STORE_SUCCESS = 1; -const SYNC_SEARCHSTATUS_STORE_REQINVALID = 2; -const SYNC_SEARCHSTATUS_STORE_SERVERERROR = 3; -const SYNC_SEARCHSTATUS_STORE_BADLINK = 4; -const SYNC_SEARCHSTATUS_STORE_ACCESSDENIED = 5; -const SYNC_SEARCHSTATUS_STORE_NOTFOUND = 6; -const SYNC_SEARCHSTATUS_STORE_CONNECTIONFAILED = 7; -const SYNC_SEARCHSTATUS_STORE_TOOCOMPLEX = 8; -const SYNC_SEARCHSTATUS_STORE_TIMEDOUT = 10; -const SYNC_SEARCHSTATUS_STORE_FOLDERSYNCREQ = 11; -const SYNC_SEARCHSTATUS_STORE_ENDOFRETRANGE = 12; -const SYNC_SEARCHSTATUS_STORE_ACCESSBLOCKED = 13; -const SYNC_SEARCHSTATUS_STORE_CREDENTIALSREQ = 14; -const SYNC_SEARCHSTATUS_PICTURE_SUCCESS = 1; -const SYNC_SEARCHSTATUS_PICTURE_NOFOTO = 173; -const SYNC_SEARCHSTATUS_PICTURE_MAXSIZEEXCEEDED = 174; -const SYNC_SEARCHSTATUS_PICTURE_MAXPICTURESEXCEEDED = 175; - -const SYNC_SETTINGSSTATUS_SUCCESS = 1; -const SYNC_SETTINGSSTATUS_PROTOCOLLERROR = 2; -const SYNC_SETTINGSSTATUS_DEVINFO_SUCCESS = 1; -const SYNC_SETTINGSSTATUS_DEVINFO_PROTOCOLLERROR = 2; -const SYNC_SETTINGSSTATUS_DEVIPASS_SUCCESS = 1; -const SYNC_SETTINGSSTATUS_DEVIPASS_PROTOCOLLERROR = 2; -const SYNC_SETTINGSSTATUS_DEVIPASS_INVALIDARGS = 3; -const SYNC_SETTINGSSTATUS_DEVIPASS_DENIED = 7; -const SYNC_SETTINGSSTATUS_USERINFO_SUCCESS = 1; -const SYNC_SETTINGSSTATUS_USERINFO_PROTOCOLLERROR = 2; - -const SYNC_SETTINGSOOF_DISABLED = 0; -const SYNC_SETTINGSOOF_GLOBAL = 1; -const SYNC_SETTINGSOOF_TIMEBASED = 2; - -const SYNC_MIMETRUNCATION_ALL = 0; -const SYNC_MIMETRUNCATION_4096 = 1; -const SYNC_MIMETRUNCATION_5120 = 2; -const SYNC_MIMETRUNCATION_7168 = 3; -const SYNC_MIMETRUNCATION_10240 = 4; -const SYNC_MIMETRUNCATION_20480 = 5; -const SYNC_MIMETRUNCATION_51200 = 6; -const SYNC_MIMETRUNCATION_102400 = 7; -const SYNC_MIMETRUNCATION_COMPLETE = 8; - -const SYNC_MIMESUPPORT_NEVER = 0; -const SYNC_MIMESUPPORT_SMIME = 1; -const SYNC_MIMESUPPORT_ALWAYS = 2; - -const SYNC_VALIDATECERTSTATUS_SUCCESS = 1; -const SYNC_VALIDATECERTSTATUS_PROTOCOLLERROR = 2; -const SYNC_VALIDATECERTSTATUS_CANTVALIDATESIG = 3; -const SYNC_VALIDATECERTSTATUS_DIGIDUNTRUSTED = 4; -const SYNC_VALIDATECERTSTATUS_CERTCHAINNOTCORRECT = 5; -const SYNC_VALIDATECERTSTATUS_DIGIDNOTVALIDFORSIGN = 6; -const SYNC_VALIDATECERTSTATUS_DIGIDNOTVALID = 7; -const SYNC_VALIDATECERTSTATUS_INVALIDCHAINCERTSTIME = 8; -const SYNC_VALIDATECERTSTATUS_DIGIDUSEDINCORRECTLY = 9; -const SYNC_VALIDATECERTSTATUS_INCORRECTDIGIDINFO = 10; -const SYNC_VALIDATECERTSTATUS_INCORRECTUSEOFDIGIDINCHAIN = 11; -const SYNC_VALIDATECERTSTATUS_DIGIDDOESNOTMATCHEMAIL = 12; -const SYNC_VALIDATECERTSTATUS_DIGIDREVOKED = 13; -const SYNC_VALIDATECERTSTATUS_DIGIDSERVERUNAVAILABLE = 14; -const SYNC_VALIDATECERTSTATUS_DIGIDINCHAINREVOKED = 15; -const SYNC_VALIDATECERTSTATUS_DIGIDREVSTATUSUNVALIDATED = 16; -const SYNC_VALIDATECERTSTATUS_SERVERERROR = 17; - -const SYNC_COMMONSTATUS_SUCCESS = 1; -const SYNC_COMMONSTATUS_INVALIDCONTENT = 101; -const SYNC_COMMONSTATUS_INVALIDWBXML = 102; -const SYNC_COMMONSTATUS_INVALIDXML = 103; -const SYNC_COMMONSTATUS_INVALIDDATETIME = 104; -const SYNC_COMMONSTATUS_INVALIDCOMBINATIONOFIDS = 105; -const SYNC_COMMONSTATUS_INVALIDIDS = 106; -const SYNC_COMMONSTATUS_INVALIDMIME = 107; -const SYNC_COMMONSTATUS_DEVIDMISSINGORINVALID = 108; -const SYNC_COMMONSTATUS_DEVTYPEMISSINGORINVALID = 109; -const SYNC_COMMONSTATUS_SERVERERROR = 110; -const SYNC_COMMONSTATUS_SERVERERRORRETRYLATER = 111; -const SYNC_COMMONSTATUS_ADACCESSDENIED = 112; -const SYNC_COMMONSTATUS_MAILBOXQUOTAEXCEEDED = 113; -const SYNC_COMMONSTATUS_MAILBOXSERVEROFFLINE = 114; -const SYNC_COMMONSTATUS_SENDQUOTAEXCEEDED = 115; -const SYNC_COMMONSTATUS_MESSRECIPUNRESOLVED = 116; -const SYNC_COMMONSTATUS_MESSREPLYNOTALLOWED = 117; -const SYNC_COMMONSTATUS_MESSPREVSENT = 118; -const SYNC_COMMONSTATUS_MESSHASNORECIP = 119; -const SYNC_COMMONSTATUS_MAILSUBMISSIONFAILED = 120; -const SYNC_COMMONSTATUS_MESSREPLYFAILED = 121; -const SYNC_COMMONSTATUS_ATTTOOLARGE = 122; -const SYNC_COMMONSTATUS_USERHASNOMAILBOX = 123; -const SYNC_COMMONSTATUS_USERCANTBEANONYMOUS = 124; -const SYNC_COMMONSTATUS_USERPRINCIPALNOTFOUND = 125; -const SYNC_COMMONSTATUS_USERDISABLEDFORSYNC = 126; -const SYNC_COMMONSTATUS_USERONNEWMAILBOXCANTSYNC = 127; -const SYNC_COMMONSTATUS_USERONLEGACYMAILBOXCANTSYNC = 128; -const SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER = 129; -const SYNC_COMMONSTATUS_ACCESSDENIED = 130; -const SYNC_COMMONSTATUS_ACCOUNTDISABLED = 131; -const SYNC_COMMONSTATUS_SYNCSTATENOTFOUND = 132; -const SYNC_COMMONSTATUS_SYNCSTATELOCKED = 133; -const SYNC_COMMONSTATUS_SYNCSTATECORRUPT = 134; -const SYNC_COMMONSTATUS_SYNCSTATEEXISTS = 135; -const SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID = 136; -const SYNC_COMMONSTATUS_COMMANDONOTSUPPORTED = 137; -const SYNC_COMMONSTATUS_VERSIONNOTSUPPORTED = 138; -const SYNC_COMMONSTATUS_DEVNOTFULLYPROVISIONABLE = 139; -const SYNC_COMMONSTATUS_REMWIPEREQUESTED = 140; -const SYNC_COMMONSTATUS_LEGACYDEVONSTRICTPOLICY = 141; -const SYNC_COMMONSTATUS_DEVICENOTPROVISIONED = 142; -const SYNC_COMMONSTATUS_POLICYREFRESH = 143; -const SYNC_COMMONSTATUS_INVALIDPOLICYKEY = 144; -const SYNC_COMMONSTATUS_EXTMANDEVICESNOTALLOWED = 145; -const SYNC_COMMONSTATUS_NORECURRINCAL = 146; -const SYNC_COMMONSTATUS_UNEXPECTEDITEMCLASS = 147; -const SYNC_COMMONSTATUS_REMSERVERHASNOSSL = 148; -const SYNC_COMMONSTATUS_INVALIDSTOREDREQ = 149; -const SYNC_COMMONSTATUS_ITEMNOTFOUND = 150; -const SYNC_COMMONSTATUS_TOOMANYFOLDERS = 151; -const SYNC_COMMONSTATUS_NOFOLDERSFOUND = 152; -const SYNC_COMMONSTATUS_ITEMLOSTAFTERMOVE = 153; -const SYNC_COMMONSTATUS_FAILUREINMOVE = 154; -const SYNC_COMMONSTATUS_NONPERSISTANTMOVEDISALLOWED = 155; -const SYNC_COMMONSTATUS_MOVEINVALIDDESTFOLDER = 156; -const SYNC_COMMONSTATUS_INVALIDACCOUNTID = 166; -const SYNC_COMMONSTATUS_ACCOUNTSENDDISABLED = 167; -const SYNC_COMMONSTATUS_IRMFEATUREDISABLED = 168; -const SYNC_COMMONSTATUS_IRMTRANSIENTERROR = 169; -const SYNC_COMMONSTATUS_IRMPERMANENTERROR = 170; -const SYNC_COMMONSTATUS_IRMINVALIDTEMPLATEID = 171; -const SYNC_COMMONSTATUS_IRMOPERATIONNOTPERMITTED = 172; -const SYNC_COMMONSTATUS_NOPICTURE = 173; -const SYNC_COMMONSTATUS_PICTURETOOLARGE = 174; -const SYNC_COMMONSTATUS_PICTURELIMITREACHED = 175; -const SYNC_COMMONSTATUS_BODYPARTCONVERSATIONTOOLARGE = 176; -const SYNC_COMMONSTATUS_MAXDEVICESREACHED = 177; - -const HTTP_CODE_200 = 200; -const HTTP_CODE_401 = 401; -const HTTP_CODE_449 = 449; -const HTTP_CODE_500 = 500; - -//logging defs -const LOGLEVEL_OFF = 0; -const LOGLEVEL_FATAL = 1; -const LOGLEVEL_ERROR = 2; -const LOGLEVEL_WARN = 4; -const LOGLEVEL_INFO = 8; -const LOGLEVEL_DEBUG = 16; -const LOGLEVEL_WBXML = 32; -const LOGLEVEL_DEVICEID = 64; -const LOGLEVEL_WBXMLSTACK = 128; - -//LOGLEVEL_ALL = LOGLEVEL_FATAL | LOGLEVEL_ERROR | LOGLEVEL_WARN | LOGLEVEL_INFO | LOGLEVEL_DEBUG | LOGLEVEL_WBXML -const LOGLEVEL_ALL = 63; - -const BACKEND_DISCARD_DATA = 1; - -const SYNC_BODYPREFERENCE_UNDEFINED = 0; -const SYNC_BODYPREFERENCE_PLAIN = 1; -const SYNC_BODYPREFERENCE_HTML = 2; -const SYNC_BODYPREFERENCE_RTF = 3; -const SYNC_BODYPREFERENCE_MIME = 4; - -const SYNC_FLAGSTATUS_CLEAR = 0; -const SYNC_FLAGSTATUS_COMPLETE = 1; -const SYNC_FLAGSTATUS_ACTIVE = 2; - -const DEFAULT_EMAIL_CONTENTCLASS = "urn:content-classes:message"; -const DEFAULT_CALENDAR_CONTENTCLASS = "urn:content-classes:calendarmessage"; - -const SYNC_MAIL_LASTVERB_UNKNOWN = 0; -const SYNC_MAIL_LASTVERB_REPLYSENDER = 1; -const SYNC_MAIL_LASTVERB_REPLYALL = 2; -const SYNC_MAIL_LASTVERB_FORWARD = 3; - -const INTERNET_CPID_WINDOWS1252 = 1252; -const INTERNET_CPID_UTF8 = 65001; - -const MAPI_E_NOT_ENOUGH_MEMORY_32BIT = -2147024882; -const MAPI_E_NOT_ENOUGH_MEMORY_64BIT = 2147942414; - -const SYNC_SETTINGSOOF_BODYTYPE_HTML = "HTML"; -const SYNC_SETTINGSOOF_BODYTYPE_TEXT = "TEXT"; - -const SYNC_FILEAS_FIRSTLAST = 1; -const SYNC_FILEAS_LASTFIRST = 2; -const SYNC_FILEAS_COMPANYONLY = 3; -const SYNC_FILEAS_COMPANYLAST = 4; -const SYNC_FILEAS_COMPANYFIRST = 5; -const SYNC_FILEAS_LASTCOMPANY = 6; -const SYNC_FILEAS_FIRSTCOMPANY = 7; - -const SYNC_RESOLVERECIPIENTS_TYPE_GAL = 1; -const SYNC_RESOLVERECIPIENTS_TYPE_CONTACT = 2; - -const SYNC_RESOLVERECIPIENTS_CERTRETRIEVE_NO = 1; -const SYNC_RESOLVERECIPIENTS_CERTRETRIEVE_FULL = 2; -const SYNC_RESOLVERECIPIENTS_CERTRETRIEVE_MINI = 3; - -const NOTEIVERB_REPLYTOSENDER = 102; -const NOTEIVERB_REPLYTOALL = 103; -const NOTEIVERB_FORWARD = 104; - -const AS_REPLYTOSENDER = 1; -const AS_REPLYTOALL = 2; -const AS_FORWARD = 3; diff --git a/sources/lib/core/zsyslog.php b/sources/lib/core/zsyslog.php deleted file mode 100644 index 8396818..0000000 --- a/sources/lib/core/zsyslog.php +++ /dev/null @@ -1,81 +0,0 @@ - 0) { - $syslog_message = "<{$pri}>" . date('M d H:i:s ') . self::$program . ': ' . $line; - socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, self::$hostname, self::$port); - } - } - socket_close($sock); - } - - return true; - } - - /** - * Converts the ZLog level to SYSLOG level. - * - * @params int $loglevel Z-Push LogLevel - * - * @access private - * @return SYSLOG_LEVEL or false - */ - private static function zlogLevel2SyslogLevel($loglevel) { - switch($loglevel) { - case LOGLEVEL_OFF: return false; break; - case LOGLEVEL_FATAL: return LOG_ALERT; break; - case LOGLEVEL_ERROR: return LOG_ERR; break; - case LOGLEVEL_WARN: return LOG_WARNING; break; - case LOGLEVEL_INFO: return LOG_INFO; break; - case LOGLEVEL_DEBUG: return LOG_DEBUG; break; - case LOGLEVEL_WBXML: return LOG_DEBUG; break; - case LOGLEVEL_DEVICEID: return LOG_DEBUG; break; - case LOGLEVEL_WBXMLSTACK: return LOG_DEBUG; break; - } - } -} \ No newline at end of file diff --git a/sources/lib/default/backend.php b/sources/lib/default/backend.php deleted file mode 100644 index ad92517..0000000 --- a/sources/lib/default/backend.php +++ /dev/null @@ -1,328 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -abstract class Backend implements IBackend { - protected $permanentStorage; - protected $stateStorage; - protected $originalUsername; - - /** - * Constructor - * - * @access public - */ - public function Backend() { - } - - /** - * Returns a IStateMachine implementation used to save states - * The default StateMachine should be used here, so, false is fine - * - * @access public - * @return boolean/object - */ - public function GetStateMachine() { - return false; - } - - /** - * Returns a ISearchProvider implementation used for searches - * the SearchProvider is just a stub - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider() { - return new SearchProvider(); - } - - /** - * Indicates which AS version is supported by the backend. - * By default AS version 2.5 (ASV_25) is returned (Z-Push 1 standard). - * Subclasses can overwrite this method to set another AS version - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion() { - return ZPush::ASV_25; - } - - /********************************************************************* - * Methods to be implemented - * - * public function Logon($username, $domain, $password); - * public function Setup($store, $checkACLonly = false, $folderid = false); - * public function Logoff(); - * public function GetHierarchy(); - * public function GetImporter($folderid = false); - * public function GetExporter($folderid = false); - * public function SendMail($sm); - * public function Fetch($folderid, $id, $contentparameters); - * public function GetWasteBasket(); - * public function GetAttachmentData($attname); - * public function MeetingResponse($requestid, $folderid, $response); - * - */ - - /** - * Deletes all contents of the specified folder. - * This is generally used to empty the trash (wastebasked), but could also be used on any - * other folder. - * - * @param string $folderid - * @param boolean $includeSubfolders (opt) also delete sub folders, default true - * - * @access public - * @return boolean - * @throws StatusException - */ - public function EmptyFolder($folderid, $includeSubfolders = true) { - return false; - } - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * - * @access public - * @return boolean - */ - public function HasChangesSink() { - return false; - } - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBacken->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if there is any problem with that folder - */ - public function ChangesSinkInitialize($folderid) { - return false; - } - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30) { - return array(); - } - - /** - * Applies settings to and gets informations from the device - * - * @param SyncObject $settings (SyncOOF or SyncUserInformation possible) - * - * @access public - * @return SyncObject $settings - */ - public function Settings($settings) { - if ($settings instanceof SyncOOF || $settings instanceof SyncUserInformation) - $settings->Status = SYNC_SETTINGSSTATUS_SUCCESS; - return $settings; - } - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients) { - $r = new SyncResolveRecipients(); - $r->status = SYNC_RESOLVERECIPSSTATUS_PROTOCOLERROR; - $r->recipient = array(); - return $r; - } - - /** - * Returns the email address and the display name of the user. Used by autodiscover. - * - * @param string $username The username - * - * @access public - * @return Array - */ - public function GetUserDetails($username) { - return array('emailaddress' => $username, 'fullname' => $username); - } - - /** - * Returns the username and store of the currently active user - * - * @access public - * @return Array - */ - public function GetCurrentUsername() { - return $this->GetUserDetails(Request::GetAuthUser()); - } - - /**---------------------------------------------------------------------------------------------------------- - * Protected methods for BackendStorage - * - * Backends can use a permanent and a state related storage to save additional data - * used during the synchronization. - * - * While permament storage is bound to the device and user, state related data works linked - * to the regular states (and its counters). - * - * Both consist of a StateObject, while the backend can decide what to save in it. - * - * Before using $this->permanentStorage and $this->stateStorage the initilize methods have to be - * called from the backend. - * - * Backend->LogOff() must call $this->SaveStorages() so the data is written to disk! - * - * These methods are an abstraction layer for StateManager->Get/SetBackendStorage() - * which can also be used independently. - */ - - /** - * Loads the permanent storage data of the user and device - * - * @access protected - * @return - */ - protected function InitializePermanentStorage() { - if (!isset($this->permanentStorage)) { - try { - $this->permanentStorage = ZPush::GetDeviceManager()->GetStateManager()->GetBackendStorage(StateManager::BACKENDSTORAGE_PERMANENT); - } - catch (StateNotYetAvailableException $snyae) { - $this->permanentStorage = new StateObject(); - } - catch(StateNotFoundException $snfe) { - $this->permanentStorage = new StateObject(); - } - } - } - - /** - * Loads the state related storage data of the user and device - * All data not necessary for the next state should be removed - * - * @access protected - * @return - */ - protected function InitializeStateStorage() { - if (!isset($this->stateStorage)) { - try { - $this->stateStorage = ZPush::GetDeviceManager()->GetStateManager()->GetBackendStorage(StateManager::BACKENDSTORAGE_STATE); - } - catch (StateNotYetAvailableException $snyae) { - $this->stateStorage = new StateObject(); - } - catch(StateNotFoundException $snfe) { - $this->stateStorage = new StateObject(); - } - } - } - - /** - * Saves the permanent and state related storage data of the user and device - * if they were loaded previousily - * If the backend storage is used this should be called - * - * @access protected - * @return - */ - protected function SaveStorages() { - if (isset($this->permanentStorage)) { - try { - ZPush::GetDeviceManager()->GetStateManager()->SetBackendStorage($this->permanentStorage, StateManager::BACKENDSTORAGE_PERMANENT); - } - catch (StateNotYetAvailableException $snyae) { } - catch(StateNotFoundException $snfe) { } - } - if (isset($this->stateStorage)) { - try { - ZPush::GetDeviceManager()->GetStateManager()->SetBackendStorage($this->stateStorage, StateManager::BACKENDSTORAGE_STATE); - } - catch (StateNotYetAvailableException $snyae) { } - catch(StateNotFoundException $snfe) { } - } - } - - /** - * Sets the username originally specified by the user to connect with Z-Push. This can be different from the - * username used for this backend; for example, BackendCombined could have applied a username mapping. - * - * This information can be used by backends to communicate the right username; for example, calendar events - * without an organizer need to supply the original username in order for the device to understand that the - * user owns the event. - * - * @param string $originalUsername The original username - */ - public function SetOriginalUsername($originalUsername) { - $this->originalUsername = $originalUsername; - } -} \ No newline at end of file diff --git a/sources/lib/default/diffbackend/diffbackend.php b/sources/lib/default/diffbackend/diffbackend.php deleted file mode 100644 index 7fd87dc..0000000 --- a/sources/lib/default/diffbackend/diffbackend.php +++ /dev/null @@ -1,368 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -abstract class BackendDiff extends Backend { - protected $store; - - /** - * Setup the backend to work on a specific store or checks ACLs there. - * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be - * performed on this store (switch operations store). - * If the ACL check is enabled, this operation should just indicate the ACL status on - * the submitted store, without changing the store for operations. - * For the ACL status, the currently logged on user MUST have access rights on - * - the entire store - admin access if no folderid is sent, or - * - on a specific folderid in the store (secretary/full access rights) - * - * The ACLcheck MUST fail if a folder of the authenticated user is checked! - * - * @param string $store target store, could contain a "domain\user" value - * @param boolean $checkACLonly if set to true, Setup() should just check ACLs - * @param string $folderid if set, only ACLs on this folderid are relevant - * - * @access public - * @return boolean - */ - public function Setup($store, $checkACLonly = false, $folderid = false) { - $this->store = $store; - - // we don't know if and how diff backends implement the "admin" check, but this will disable it for the webservice - // backends which want to implement this, need to overwrite this method explicitely. For more info see https://jira.zarafa.com/browse/ZP-462 - if ($store == "SYSTEM" && $checkACLonly == true) - return false; - - return true; - } - - /** - * Returns an array of SyncFolder types with the entire folder hierarchy - * on the server (the array itself is flat, but refers to parents via the 'parent' property - * - * provides AS 1.0 compatibility - * - * @access public - * @return array SYNC_FOLDER - */ - function GetHierarchy() { - $folders = array(); - - $fl = $this->GetFolderList(); - if (is_array($fl)) - foreach($fl as $f) - $folders[] = $this->GetFolder($f['id']); - - return $folders; - } - - /** - * Returns the importer to process changes from the mobile - * If no $folderid is given, hierarchy importer is expected - * - * @param string $folderid (opt) - * - * @access public - * @return object(ImportChanges) - * @throws StatusException - */ - public function GetImporter($folderid = false) { - return new ImportChangesDiff($this, $folderid); - } - - /** - * Returns the exporter to send changes to the mobile - * If no $folderid is given, hierarchy exporter is expected - * - * @param string $folderid (opt) - * - * @access public - * @return object(ExportChanges) - * @throws StatusException - */ - public function GetExporter($folderid = false) { - return new ExportChangesDiff($this, $folderid); - } - - /** - * Returns all available data of a single message - * - * @param string $folderid - * @param string $id - * @param ContentParameters $contentparameters flag - * - * @access public - * @return object(SyncObject) - * @throws StatusException - */ - public function Fetch($folderid, $id, $contentparameters) { - // override truncation - $contentparameters->SetTruncation(SYNC_TRUNCATION_ALL); - $msg = $this->GetMessage($folderid, $id, $contentparameters); - if ($msg === false) - throw new StatusException("BackendDiff->Fetch('%s','%s'): Error, unable retrieve message from backend", SYNC_STATUS_OBJECTNOTFOUND); - return $msg; - } - - /** - * Processes a response to a meeting request. - * CalendarID is a reference and has to be set if a new calendar item is created - * - * @param string $requestid id of the object containing the request - * @param string $folderid id of the parent folder of $requestid - * @param string $response - * - * @access public - * @return string id of the created/updated calendar obj - * @throws StatusException - */ - public function MeetingResponse($requestid, $folderid, $response) { - throw new StatusException(sprintf("BackendDiff->MeetingResponse('%s','%s','%s'): Error, this functionality is not supported by the diff backend", $requestid, $folderid, $response), SYNC_MEETRESPSTATUS_MAILBOXERROR); - } - - /**---------------------------------------------------------------------------------------------------------- - * Abstract DiffBackend methods - * - * Need to be implemented in the actual diff backend - */ - - /** - * Returns a list (array) of folders, each entry being an associative array - * with the same entries as StatFolder(). This method should return stable information; ie - * if nothing has changed, the items in the array must be exactly the same. The order of - * the items within the array is not important though. - * - * @access protected - * @return array/boolean false if the list could not be retrieved - */ - public abstract function GetFolderList(); - - /** - * Returns an actual SyncFolder object with all the properties set. Folders - * are pretty simple, having only a type, a name, a parent and a server ID. - * - * @param string $id id of the folder - * - * @access public - * @return object SyncFolder with information - */ - public abstract function GetFolder($id); - - /** - * Returns folder stats. An associative array with properties is expected. - * - * @param string $id id of the folder - * - * @access public - * @return array - * Associative array( - * string "id" The server ID that will be used to identify the folder. It must be unique, and not too long - * How long exactly is not known, but try keeping it under 20 chars or so. It must be a string. - * string "parent" The server ID of the parent of the folder. Same restrictions as 'id' apply. - * long "mod" This is the modification signature. It is any arbitrary string which is constant as long as - * the folder has not changed. In practice this means that 'mod' can be equal to the folder name - * as this is the only thing that ever changes in folders. (the type is normally constant) - * ) - */ - public abstract function StatFolder($id); - - /** - * Creates or modifies a folder - * - * @param string $folderid id of the parent folder - * @param string $oldid if empty -> new folder created, else folder is to be renamed - * @param string $displayname new folder name (to be created, or to be renamed to) - * @param int $type folder type - * - * @access public - * @return boolean status - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - * - */ - public abstract function ChangeFolder($folderid, $oldid, $displayname, $type); - - /** - * Deletes a folder - * - * @param string $id - * @param string $parent is normally false - * - * @access public - * @return boolean status - false if e.g. does not exist - * @throws StatusException could throw specific SYNC_FSSTATUS_* exceptions - */ - public abstract function DeleteFolder($id, $parentid); - - /** - * Returns a list (array) of messages, each entry being an associative array - * with the same entries as StatMessage(). This method should return stable information; ie - * if nothing has changed, the items in the array must be exactly the same. The order of - * the items within the array is not important though. - * - * The $cutoffdate is a date in the past, representing the date since which items should be shown. - * This cutoffdate is determined by the user's setting of getting 'Last 3 days' of e-mail, etc. If - * the cutoffdate is ignored, the user will not be able to select their own cutoffdate, but all - * will work OK apart from that. - * - * @param string $folderid id of the parent folder - * @param long $cutoffdate timestamp in the past from which on messages should be returned - * - * @access public - * @return array/false array with messages or false if folder is not available - */ - public abstract function GetMessageList($folderid, $cutoffdate); - - /** - * Returns the actual SyncXXX object type. The '$folderid' of parent folder can be used. - * Mixing item types returned is illegal and will be blocked by the engine; ie returning an Email object in a - * Tasks folder will not do anything. The SyncXXX objects should be filled with as much information as possible, - * but at least the subject, body, to, from, etc. - * - * @param string $folderid id of the parent folder - * @param string $id id of the message - * @param ContentParameters $contentparameters parameters of the requested message (truncation, mimesupport etc) - * - * @access public - * @return object/false false if the message could not be retrieved - */ - public abstract function GetMessage($folderid, $id, $contentparameters); - - /** - * Returns message stats, analogous to the folder stats from StatFolder(). - * - * @param string $folderid id of the folder - * @param string $id id of the message - * - * @access public - * @return array or boolean if fails - * Associative array( - * string "id" Server unique identifier for the message. Again, try to keep this short (under 20 chars) - * int "flags" simply '0' for unread, '1' for read - * long "mod" This is the modification signature. It is any arbitrary string which is constant as long as - * the message has not changed. As soon as this signature changes, the item is assumed to be completely - * changed, and will be sent to the PDA as a whole. Normally you can use something like the modification - * time for this field, which will change as soon as the contents have changed. - * ) - */ - public abstract function StatMessage($folderid, $id); - - /** - * Called when a message has been changed on the mobile. The new message must be saved to disk. - * The return value must be whatever would be returned from StatMessage() after the message has been saved. - * This way, the 'flags' and the 'mod' properties of the StatMessage() item may change via ChangeMessage(). - * This method will never be called on E-mail items as it's not 'possible' to change e-mail items. It's only - * possible to set them as 'read' or 'unread'. - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param SyncXXX $message the SyncObject containing a message - * @param ContentParameters $contentParameters - * - * @access public - * @return array same return value as StatMessage() - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public abstract function ChangeMessage($folderid, $id, $message, $contentParameters); - - /** - * Changes the 'read' flag of a message on disk. The $flags - * parameter can only be '1' (read) or '0' (unread). After a call to - * SetReadFlag(), GetMessageList() should return the message with the - * new 'flags' but should not modify the 'mod' parameter. If you do - * change 'mod', simply setting the message to 'read' on the mobile will trigger - * a full resync of the item from the server. - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param int $flags read flag of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public abstract function SetReadFlag($folderid, $id, $flags, $contentParameters); - - /** - * Called when the user has requested to delete (really delete) a message. Usually - * this means just unlinking the file its in or somesuch. After this call has succeeded, a call to - * GetMessageList() should no longer list the message. If it does, the message will be re-sent to the mobile - * as it will be seen as a 'new' item. This means that if this method is not implemented, it's possible to - * delete messages on the PDA, but as soon as a sync is done, the item will be resynched to the mobile - * - * @param string $folderid id of the folder - * @param string $id id of the message - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_STATUS_* exceptions - */ - public abstract function DeleteMessage($folderid, $id, $contentParameters); - - /** - * Called when the user moves an item on the PDA from one folder to another. Whatever is needed - * to move the message on disk has to be done here. After this call, StatMessage() and GetMessageList() - * should show the items to have a new parent. This means that it will disappear from GetMessageList() - * of the sourcefolder and the destination folder will show the new message - * - * @param string $folderid id of the source folder - * @param string $id id of the message - * @param string $newfolderid id of the destination folder - * @param ContentParameters $contentParameters - * - * @access public - * @return boolean status of the operation - * @throws StatusException could throw specific SYNC_MOVEITEMSSTATUS_* exceptions - */ - public abstract function MoveMessage($folderid, $id, $newfolderid, $contentParameters); - -} diff --git a/sources/lib/default/diffbackend/diffstate.php b/sources/lib/default/diffbackend/diffstate.php deleted file mode 100644 index 07f1731..0000000 --- a/sources/lib/default/diffbackend/diffstate.php +++ /dev/null @@ -1,314 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class DiffState implements IChanges { - protected $syncstate; - protected $backend; - protected $flags; - protected $contentparameters; - protected $cutoffdate; - - /** - * Initializes the state - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean status flag - * @throws StatusException - */ - public function Config($state, $flags = 0) { - if ($state == "") - $state = array(); - - if (!is_array($state)) - throw new StatusException("Invalid state", SYNC_FSSTATUS_CODEUNKNOWN); - - $this->syncstate = $state; - $this->flags = $flags; - return true; - } - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters) { - $this->contentparameters = $contentparameters; - - $filtertype = $contentparameters->GetFilterType(); - switch($contentparameters->GetContentClass()) { - case "Email": - case "Calendar": - $this->cutoffdate = ($filtertype === false) ? 0 : Utils::GetCutOffDate($filtertype); - break; - case "Contacts": - case "Tasks": - default: - $this->cutoffdate = 0; - break; - } - } - - /** - * Returns state - * - * @access public - * @return string - * @throws StatusException - */ - public function GetState() { - if (!isset($this->syncstate) || !is_array($this->syncstate)) - throw new StatusException("DiffState->GetState(): Error, state not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); - - return $this->syncstate; - } - - - /**---------------------------------------------------------------------------------------------------------- - * DiffState specific stuff - */ - - /** - * Comparing function used for sorting of the differential engine - * - * @param array $a - * @param array $b - * - * @access public - * @return boolean - */ - static public function RowCmp($a, $b) { - return strcmp($b['id'], $a['id']); - } - - /** - * Differential mechanism - * Compares the current syncstate to the sent $new - * - * @param array $new - * - * @access protected - * @return array - */ - protected function getDiffTo($new) { - $changes = array(); - - // Sort both arrays in the same way by ID - usort($this->syncstate, array("DiffState", "RowCmp")); - usort($new, array("DiffState", "RowCmp")); - - $inew = 0; - $iold = 0; - $cntstate = count($this->syncstate); - $cntnew = count($new); - - // Get changes by comparing our list of messages with - // our previous state - while(true) { - if($iold >= $cntstate || $inew >= $cntnew) - break; - - $cmp = strcmp($this->syncstate[$iold]["id"], $new[$inew]["id"]); - if ($cmp == 0) { - // Both messages are still available, compare flags and mod - if(isset($this->syncstate[$iold]["flags"]) && isset($new[$inew]["flags"]) && $this->syncstate[$iold]["flags"] != $new[$inew]["flags"]) { - // Flags changed - $change = array(); - $change["type"] = "flags"; - $change["id"] = $new[$inew]["id"]; - $change["flags"] = $new[$inew]["flags"]; - $changes[] = $change; - } - - if(isset($this->syncstate[$iold]["mod"]) && isset($new[$inew]["mod"]) && $this->syncstate[$iold]["mod"] != $new[$inew]["mod"]) { - $change = array(); - $change["type"] = "change"; - $change["id"] = $new[$inew]["id"]; - $changes[] = $change; - } - - $inew++; - $iold++; - } elseif ($cmp > 0) { - // Message in state seems to have disappeared (delete) - $change = array(); - $change["type"] = "delete"; - $change["id"] = $this->syncstate[$iold]["id"]; - $changes[] = $change; - $iold++; - } else { - // Message in new seems to be new (add) - $change = array(); - $change["type"] = "change"; - $change["flags"] = SYNC_NEWMESSAGE; - $change["id"] = $new[$inew]["id"]; - $changes[] = $change; - $inew++; - } - } - - while($iold < $cntstate) { - // All data left in 'syncstate' have been deleted - $change = array(); - $change["type"] = "delete"; - $change["id"] = $this->syncstate[$iold]["id"]; - $changes[] = $change; - $iold++; - } - - while($inew < $cntnew) { - // All data left in new have been added - $change = array(); - $change["type"] = "change"; - $change["flags"] = SYNC_NEWMESSAGE; - $change["id"] = $new[$inew]["id"]; - $changes[] = $change; - $inew++; - } - - return $changes; - } - - /** - * Update the state to reflect changes - * - * @param string $type of change - * @param array $change - * - * - * @access protected - * @return - */ - protected function updateState($type, $change) { - // Change can be a change or an add - $change_id = $change['id']; - foreach ($this->syncstate as $i => &$state) { - if($this->syncstate[$i]["id"] == $change["id"]) { - if ($state['id'] == $change_id) { - $this->syncstate[$i] = $change; - switch ($type) { - case 'change': - $state = $change; - return; - case 'flags': - $state['flags'] = $change['flags']; - return; - case 'delete': - array_splice($this->syncstate, $i, 1); - return; - default: - throw new Exception(sprintf("updateState: type '%s' is not supported", $type)); - } - } - } - } - if($type == "change") { - $this->syncstate[] = $change; - } else { - $flags = empty($change['flags'])?"":$change['flags']; - $mod = empty($change['mod'])?"":$change['mod']; - ZLog::Write(LOGLEVEL_WARN, sprintf("updateState: no state modification!!! %s|%s|%s|%s", $type, $change_id, $flags, $mod)); - } - } - - /** - * Returns TRUE if the given ID conflicts with the given operation. This is only true in the following situations: - * - Changed here and changed there - * - Changed here and deleted there - * - Deleted here and changed there - * Any other combination of operations can be done (e.g. change flags & move or move & delete) - * - * @param string $type of change - * @param string $folderid - * @param string $id - * - * @access protected - * @return - */ - protected function isConflict($type, $folderid, $id) { - $stat = $this->backend->StatMessage($folderid, $id); - - if(!$stat) { - // Message is gone - if($type == "change") - return true; // deleted here, but changed there - else - return false; // all other remote changes still result in a delete (no conflict) - } - - foreach($this->syncstate as $state) { - if($state["id"] == $id) { - $oldstat = $state; - break; - } - } - - if(!isset($oldstat)) { - // New message, can never conflict - return false; - } - - if($stat["mod"] != $oldstat["mod"]) { - // Changed here - if($type == "delete" || $type == "change") - return true; // changed here, but deleted there -> conflict, or changed here and changed there -> conflict - else - return false; // changed here, and other remote changes (move or flags) - } - } - -} diff --git a/sources/lib/default/diffbackend/exportchangesdiff.php b/sources/lib/default/diffbackend/exportchangesdiff.php deleted file mode 100644 index e159229..0000000 --- a/sources/lib/default/diffbackend/exportchangesdiff.php +++ /dev/null @@ -1,216 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ExportChangesDiff extends DiffState implements IExportChanges{ - private $importer; - private $folderid; - private $changes; - private $step; - - /** - * Constructor - * - * @param object $backend - * @param string $folderid - * - * @access public - * @throws StatusException - */ - public function ExportChangesDiff($backend, $folderid) { - $this->backend = $backend; - $this->folderid = $folderid; - } - - /** - * Sets the importer the exporter will sent it's changes to - * and initializes the Exporter - * - * @param object &$importer Implementation of IImportChanges - * - * @access public - * @return boolean - * @throws StatusException - */ - public function InitializeExporter(&$importer) { - $this->changes = array(); - $this->step = 0; - $this->importer = $importer; - - if($this->folderid) { - // Get the changes since the last sync - if(!isset($this->syncstate) || !$this->syncstate) - $this->syncstate = array(); - - ZLog::Write(LOGLEVEL_DEBUG,sprintf("ExportChangesDiff->InitializeExporter(): Initializing message diff engine. '%d' messages in state", count($this->syncstate))); - - //do nothing if it is a dummy folder - if ($this->folderid != SYNC_FOLDER_TYPE_DUMMY) { - // Get our lists - syncstate (old) and msglist (new) - $msglist = $this->backend->GetMessageList($this->folderid, $this->cutoffdate); - // if the folder was deleted, no information is available anymore. A hierarchysync should be executed - if($msglist === false) - throw new StatusException("ExportChangesDiff->InitializeExporter(): Error, no message list available from the backend", SYNC_STATUS_FOLDERHIERARCHYCHANGED, null, LOGLEVEL_INFO); - - $this->changes = $this->getDiffTo($msglist); - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "Initializing folder diff engine"); - - ZLog::Write(LOGLEVEL_DEBUG, "ExportChangesDiff->InitializeExporter(): Initializing folder diff engine"); - - $folderlist = $this->backend->GetFolderList(); - if($folderlist === false) - throw new StatusException("ExportChangesDiff->InitializeExporter(): error, no folders available from the backend", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN); - - if(!isset($this->syncstate) || !$this->syncstate) - $this->syncstate = array(); - - $this->changes = $this->getDiffTo($folderlist); - } - - ZLog::Write(LOGLEVEL_INFO, sprintf("ExportChangesDiff->InitializeExporter(): Found '%d' changes for '%s'", count($this->changes), ($this->folderid)?$this->folderid : 'hierarchy' )); - } - - /** - * Returns the amount of changes to be exported - * - * @access public - * @return int - */ - public function GetChangeCount() { - return count($this->changes); - } - - /** - * Synchronizes a change - * - * @access public - * @return array - */ - public function Synchronize() { - $progress = array(); - - // Get one of our stored changes and send it to the importer, store the new state if - // it succeeds - if($this->folderid == false) { - if($this->step < count($this->changes)) { - $change = $this->changes[$this->step]; - - switch($change["type"]) { - case "change": - $folder = $this->backend->GetFolder($change["id"]); - $stat = $this->backend->StatFolder($change["id"]); - - if(!$folder) - return; - - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportFolderChange($folder)) - $this->updateState("change", $stat); - break; - case "delete": - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportFolderDeletion($change["id"])) - $this->updateState("delete", $change); - break; - } - - $this->step++; - - $progress = array(); - $progress["steps"] = count($this->changes); - $progress["progress"] = $this->step; - - return $progress; - } else { - return false; - } - } - else { - if($this->step < count($this->changes)) { - $change = $this->changes[$this->step]; - - switch($change["type"]) { - case "change": - // Note: because 'parseMessage' and 'statMessage' are two seperate - // calls, we have a chance that the message has changed between both - // calls. This may cause our algorithm to 'double see' changes. - - $stat = $this->backend->StatMessage($this->folderid, $change["id"]); - $message = $this->backend->GetMessage($this->folderid, $change["id"], $this->contentparameters); - - // copy the flag to the message - $message->flags = (isset($change["flags"])) ? $change["flags"] : 0; - - if($stat && $message) { - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageChange($change["id"], $message) == true) - $this->updateState("change", $stat); - } - break; - case "delete": - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageDeletion($change["id"]) == true) - $this->updateState("delete", $change); - break; - case "flags": - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageReadFlag($change["id"], $change["flags"]) == true) - $this->updateState("flags", $change); - break; - case "move": - if($this->flags & BACKEND_DISCARD_DATA || $this->importer->ImportMessageMove($change["id"], $change["parent"]) == true) - $this->updateState("move", $change); - break; - } - - $this->step++; - - $progress = array(); - $progress["steps"] = count($this->changes); - $progress["progress"] = $this->step; - - return $progress; - } else { - return false; - } - } - } -} diff --git a/sources/lib/default/diffbackend/importchangesdiff.php b/sources/lib/default/diffbackend/importchangesdiff.php deleted file mode 100644 index 733fc8c..0000000 --- a/sources/lib/default/diffbackend/importchangesdiff.php +++ /dev/null @@ -1,278 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ImportChangesDiff extends DiffState implements IImportChanges { - private $folderid; - - /** - * Constructor - * - * @param object $backend - * @param string $folderid - * - * @access public - * @throws StatusException - */ - public function ImportChangesDiff($backend, $folderid = false) { - $this->backend = $backend; - $this->folderid = $folderid; - } - - /** - * Would load objects which are expected to be exported with this state - * The DiffBackend implements conflict detection on the fly - * - * @param ContentParameters $contentparameters class of objects - * @param string $state - * - * @access public - * @return boolean - * @throws StatusException - */ - public function LoadConflicts($contentparameters, $state) { - // changes are detected on the fly - return true; - } - - /** - * Imports a single message - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean/string - failure / id of message - * @throws StatusException - */ - public function ImportMessageChange($id, $message) { - //do nothing if it is in a dummy folder - if ($this->folderid == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageChange('%s','%s'): can not be done on a dummy folder", $id, get_class($message)), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - if($id) { - // See if there's a conflict - $conflict = $this->isConflict("change", $this->folderid, $id); - - // Update client state if this is an update - $change = array(); - $change["id"] = $id; - $change["mod"] = 0; // dummy, will be updated later if the change succeeds - $change["parent"] = $this->folderid; - $change["flags"] = (isset($message->read)) ? $message->read : 0; - $this->updateState("change", $change); - - if($conflict && $this->flags == SYNC_CONFLICT_OVERWRITE_PIM) - // in these cases the status SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT should be returned, so the mobile client can inform the end user - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageChange('%s','%s'): Conflict detected. Data from PIM will be dropped! Server overwrites PIM. User is informed.", $id, get_class($message)), SYNC_STATUS_CONFLICTCLIENTSERVEROBJECT, null, LOGLEVEL_INFO); - } - - $stat = $this->backend->ChangeMessage($this->folderid, $id, $message, $this->contentparameters); - - if(!is_array($stat)) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageChange('%s','%s'): unknown error in backend", $id, get_class($message)), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - // Record the state of the message - $this->updateState("change", $stat); - - return $stat["id"]; - } - - /** - * Imports a deletion. This may conflict if the local object has been modified - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageDeletion($id) { - //do nothing if it is in a dummy folder - if ($this->folderid == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageDeletion('%s'): can not be done on a dummy folder", $id), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - // See if there's a conflict - $conflict = $this->isConflict("delete", $this->folderid, $id); - - // Update client state - $change = array(); - $change["id"] = $id; - $this->updateState("delete", $change); - - // If there is a conflict, and the server 'wins', then return without performing the change - // this will cause the exporter to 'see' the overriding item as a change, and send it back to the PIM - if($conflict && $this->flags == SYNC_CONFLICT_OVERWRITE_PIM) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ImportChangesDiff->ImportMessageDeletion('%s'): Conflict detected. Data from PIM will be dropped! Object was deleted.", $id)); - return false; - } - - $stat = $this->backend->DeleteMessage($this->folderid, $id, $this->contentparameters); - if(!$stat) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageDeletion('%s'): Unknown error in backend", $id), SYNC_STATUS_OBJECTNOTFOUND); - - return true; - } - - /** - * Imports a change in 'read' flag - * This can never conflict - * - * @param string $id - * @param int $flags - read/unread - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageReadFlag($id, $flags) { - //do nothing if it is a dummy folder - if ($this->folderid == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageReadFlag('%s','%s'): can not be done on a dummy folder", $id, $flags), SYNC_STATUS_SYNCCANNOTBECOMPLETED); - - // Update client state - $change = array(); - $change["id"] = $id; - $change["flags"] = $flags; - $this->updateState("flags", $change); - - $stat = $this->backend->SetReadFlag($this->folderid, $id, $flags, $this->contentparameters); - if (!$stat) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageReadFlag('%s','%s'): Error, unable retrieve message from backend", $id, $flags), SYNC_STATUS_OBJECTNOTFOUND); - - return true; - } - - /** - * Imports a move of a message. This occurs when a user moves an item to another folder - * - * @param string $id - * @param string $newfolder - * - * @access public - * @return string - * @throws StatusException - */ - public function ImportMessageMove($id, $newfolder) { - // don't move messages from or to a dummy folder (GetHierarchy compatibility) - if ($this->folderid == SYNC_FOLDER_TYPE_DUMMY || $newfolder == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportMessageMove('%s'): can not be done on a dummy folder", $id), SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - $newid = $this->backend->MoveMessage($this->folderid, $id, $newfolder, $this->contentparameters); - if ($newid === false) - throw new StatusException("ImportChangesDiff->ImportMessageMove($id, $newfolder): MoveMessage failed (false)", SYNC_MOVEITEMSSTATUS_CANNOTMOVE); - - // Don't resync the folder here, since this can be called from the combined backed and $newfolder will not exist (backend prefix is missing) - return $newid; - } - - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return string id of the folder - * @throws StatusException - */ - public function ImportFolderChange($folder) { - $id = $folder->serverid; - $parent = $folder->parentid; - $displayname = $folder->displayname; - $type = $folder->type; - - //do nothing if it is a dummy folder - if ($parent == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportFolderChange('%s'): can not be done on a dummy folder", $id), SYNC_FSSTATUS_SERVERERROR); - - if($id) { - $change = array(); - $change["id"] = $id; - $change["mod"] = $displayname; - $change["parent"] = $parent; - $change["flags"] = 0; - $this->updateState("change", $change); - } - - $stat = $this->backend->ChangeFolder($parent, $id, $displayname, $type); - - if($stat) - $this->updateState("change", $stat); - - return $stat["id"]; - } - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent id - * - * @access public - * @return int SYNC_FOLDERHIERARCHY_STATUS - * @throws StatusException - */ - public function ImportFolderDeletion($id, $parent = false) { - //do nothing if it is a dummy folder - if ($parent == SYNC_FOLDER_TYPE_DUMMY) - throw new StatusException(sprintf("ImportChangesDiff->ImportFolderDeletion('%s','%s'): can not be done on a dummy folder", $id, $parent), SYNC_FSSTATUS_SERVERERROR); - - // check the foldertype - $folder = $this->backend->GetFolder($id); - if (isset($folder->type) && Utils::IsSystemFolder($folder->type)) - throw new StatusException(sprintf("ImportChangesDiff->ImportFolderDeletion('%s','%s'): Error deleting system/default folder", $id, $parent), SYNC_FSSTATUS_SYSTEMFOLDER); - - $ret = $this->backend->DeleteFolder($id, $parent); - if (!$ret) - throw new StatusException(sprintf("ImportChangesDiff->ImportFolderDeletion('%s','%s'): can not be done on a dummy folder", $id, $parent), SYNC_FSSTATUS_FOLDERDOESNOTEXIST); - - $change = array(); - $change["id"] = $id; - - $this->updateState("delete", $change); - - return true; - } -} diff --git a/sources/lib/default/filestatemachine.php b/sources/lib/default/filestatemachine.php deleted file mode 100644 index 8322ae3..0000000 --- a/sources/lib/default/filestatemachine.php +++ /dev/null @@ -1,705 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FileStateMachine implements IStateMachine { - const SUPPORTED_STATE_VERSION = IStateMachine::STATEVERSION_02; - const VERSION = "version"; - - private $userfilename; - private $settingsfilename; - private $usermapfilename; - - /** - * Constructor - * - * Performs some basic checks and initilizes the state directory - * - * @access public - * @throws FatalMisconfigurationException - */ - public function FileStateMachine() { - if (!defined('STATE_DIR')) - throw new FatalMisconfigurationException("No configuration for the state directory available."); - - if (substr(STATE_DIR, -1,1) != "/") - throw new FatalMisconfigurationException("The configured state directory should terminate with a '/'"); - - if (!file_exists(STATE_DIR)) - throw new FatalMisconfigurationException("The configured state directory does not exist or can not be accessed: ". STATE_DIR); - // checks if the directory exists and tries to create the necessary subfolders if they do not exist - $this->getDirectoryForDevice(Request::GetDeviceID()); - $this->userfilename = STATE_DIR . 'users'; - $this->settingsfilename = STATE_DIR . 'settings'; - $this->usermapfilename = STATE_DIR . 'usermap'; - - if ((!file_exists($this->userfilename) && !touch($this->userfilename)) || !is_writable($this->userfilename)) - throw new FatalMisconfigurationException("Not possible to write to the configured state directory."); - Utils::FixFileOwner($this->userfilename); - } - - /** - * Gets a hash value indicating the latest dataset of the named - * state with a specified key and counter. - * If the state is changed between two calls of this method - * the returned hash should be different - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * - * @access public - * @return string - * @throws StateNotFoundException, StateInvalidException - */ - public function GetStateHash($devid, $type, $key = false, $counter = false) { - $filename = $this->getFullFilePath($devid, $type, $key, $counter); - - // the filemodification time is enough to track changes - if(file_exists($filename)) - return filemtime($filename); - else - throw new StateNotFoundException(sprintf("FileStateMachine->GetStateHash(): Could not locate state '%s'",$filename)); - } - - /** - * Gets a state for a specified key and counter. - * This method sould call IStateMachine->CleanStates() - * to remove older states (same key, previous counters) - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * @param string $cleanstates (opt) - * - * @access public - * @return mixed - * @throws StateNotFoundException, StateInvalidException - */ - public function GetState($devid, $type, $key = false, $counter = false, $cleanstates = true) { - if ($counter && $cleanstates) - $this->CleanStates($devid, $type, $key, $counter); - - // Read current sync state - $filename = $this->getFullFilePath($devid, $type, $key, $counter); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->GetState() on file: '%s'", $filename)); - - if(file_exists($filename)) { - return unserialize(file_get_contents($filename)); - } - // throw an exception on all other states, but not FAILSAVE as it's most of the times not there by default - else if ($type !== IStateMachine::FAILSAVE) - throw new StateNotFoundException(sprintf("FileStateMachine->GetState(): Could not locate state '%s'",$filename)); - } - - /** - * Writes ta state to for a key and counter - * - * @param mixed $state - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param int $counter (opt) - * - * @access public - * @return boolean - * @throws StateInvalidException - */ - public function SetState($state, $devid, $type, $key = false, $counter = false) { - $state = serialize($state); - - $filename = $this->getFullFilePath($devid, $type, $key, $counter); - if (($bytes = Utils::safe_put_contents($filename, $state)) === false) - throw new FatalMisconfigurationException(sprintf("FileStateMachine->SetState(): Could not write state '%s'",$filename)); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->SetState() written %d bytes on file: '%s'", $bytes, $filename)); - return $bytes; - } - - /** - * Cleans up all older states - * If called with a $counter, all states previous state counter can be removed - * If called without $counter, all keys (independently from the counter) can be removed - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key - * @param string $counter (opt) - * - * @access public - * @return - * @throws StateInvalidException - */ - public function CleanStates($devid, $type, $key, $counter = false) { - $matching_files = glob($this->getFullFilePath($devid, $type, $key). "*", GLOB_NOSORT); - if (is_array($matching_files)) { - foreach($matching_files as $state) { - $file = false; - if($counter !== false && preg_match('/([0-9]+)$/', $state, $matches)) { - if($matches[1] < $counter) { - $candidate = $this->getFullFilePath($devid, $type, $key, (int)$matches[1]); - - if ($candidate == $state) - $file = $candidate; - } - } - else if ($counter === false) - $file = $this->getFullFilePath($devid, $type, $key); - - if ($file !== false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->CleanStates(): Deleting file: '%s'", $file)); - unlink ($file); - } - } - } - } - - /** - * Links a user to a device - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean indicating if the user was added or not (existed already) - */ - public function LinkUserDevice($username, $devid) { - $mutex = new SimpleMutex(__FILE__); - $changed = false; - - // exclusive block - if ($mutex->Block()) { - $filecontents = @file_get_contents($this->userfilename); - - if ($filecontents) - $users = unserialize($filecontents); - else - $users = array(); - - // add user/device to the list - if (!isset($users[$username])) { - $users[$username] = array(); - $changed = true; - } - if (!isset($users[$username][$devid])) { - $users[$username][$devid] = 1; - $changed = true; - } - - if ($changed) { - $bytes = Utils::safe_put_contents($this->userfilename, serialize($users)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->LinkUserDevice(): wrote %d bytes to users file", $bytes)); - } - else - ZLog::Write(LOGLEVEL_DEBUG, "FileStateMachine->LinkUserDevice(): nothing changed"); - - $mutex->Release(); - } - return $changed; - } - - /** - * Unlinks a device from a user - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean - */ - public function UnLinkUserDevice($username, $devid) { - $mutex = new SimpleMutex(__FILE__); - $changed = false; - - // exclusive block - if ($mutex->Block()) { - $filecontents = @file_get_contents($this->userfilename); - - if ($filecontents) - $users = unserialize($filecontents); - else - $users = array(); - - // is this user listed at all? - if (isset($users[$username])) { - if (isset($users[$username][$devid])) { - unset($users[$username][$devid]); - $changed = true; - } - - // if there is no device left, remove the user - if (empty($users[$username])) { - unset($users[$username]); - $changed = true; - } - } - - if ($changed) { - $bytes = Utils::safe_put_contents($this->userfilename, serialize($users)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->UnLinkUserDevice(): wrote %d bytes to users file", $bytes)); - } - else - ZLog::Write(LOGLEVEL_DEBUG, "FileStateMachine->UnLinkUserDevice(): nothing changed"); - - $mutex->Release(); - } - return $changed; - } - - /** - * Get all UserDevice mapping - * - * @access public - * @return array - */ - public function GetAllUserDevice() { - return unserialize(file_get_contents($this->userfilename))?:array(); - } - - /** - * Returns an array with all device ids for a user. - * If no user is set, all device ids should be returned - * - * @param string $username (opt) - * - * @access public - * @return array - */ - public function GetAllDevices($username = false) { - $out = array(); - if ($username === false) { - foreach (glob(STATE_DIR. "/*/*/*-".IStateMachine::DEVICEDATA, GLOB_NOSORT) as $devdata) - if (preg_match('/\/([A-Za-z0-9]+)-'. IStateMachine::DEVICEDATA. '$/', $devdata, $matches)) - $out[] = $matches[1]; - return $out; - } - else { - $filecontents = file_get_contents($this->userfilename); - if ($filecontents) - $users = unserialize($filecontents); - else - $users = array(); - - // get device list for the user - if (isset($users[$username])) - return array_keys($users[$username]); - else - return array(); - } - } - - /** - * Returns the current version of the state files - * - * @access public - * @return int - */ - public function GetStateVersion() { - if (file_exists($this->settingsfilename)) { - $settings = unserialize(file_get_contents($this->settingsfilename)); - if (strtolower(gettype($settings) == "string") && strtolower($settings) == '2:1:{s:7:"version";s:1:"2";}') { - ZLog::Write(LOGLEVEL_INFO, "Broken state version file found. Attempt to autofix it. See https://jira.zarafa.com/browse/ZP-493 for more information."); - unlink($this->settingsfilename); - $this->SetStateVersion(IStateMachine::STATEVERSION_02); - $settings = array(self::VERSION => IStateMachine::STATEVERSION_02); - } - } - else { - $filecontents = @file_get_contents($this->userfilename); - if ($filecontents) - $settings = array(self::VERSION => IStateMachine::STATEVERSION_01); - else { - $settings = array(self::VERSION => self::SUPPORTED_STATE_VERSION); - $this->SetStateVersion(self::SUPPORTED_STATE_VERSION); - } - } - - return $settings[self::VERSION]; - } - - /** - * Sets the current version of the state files - * - * @param int $version the new supported version - * - * @access public - * @return boolean - */ - public function SetStateVersion($version) { - if (file_exists($this->settingsfilename)) - $settings = unserialize(file_get_contents($this->settingsfilename)); - else - $settings = array(self::VERSION => IStateMachine::STATEVERSION_01); - - $settings[self::VERSION] = $version; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->SetStateVersion() saving supported state version, value '%d'", $version)); - $status = Utils::safe_put_contents($this->settingsfilename, serialize($settings)); - Utils::FixFileOwner($this->settingsfilename); - return $status; - } - - /** - * Returns all available states for a device id - * - * @param string $devid the device id - * - * @access public - * @return array(mixed) - */ - public function GetAllStatesForDevice($devid) { - $types = array(IStateMachine::DEVICEDATA, IStateMachine::FOLDERDATA, IStateMachine::FAILSAVE, IStateMachine::HIERARCHY, IStateMachine::BACKENDSTORAGE); - $typematch = implode("|", $types); - $out = array(); - $devdir = $this->getDirectoryForDevice($devid) . "/$devid-"; - - foreach (glob($devdir . "*", GLOB_NOSORT) as $devdata) { - $str = substr($devdata, strlen($devdir)-1); - $matches = array(); - - if (!preg_match("/^(?:-(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}))?(?:-($typematch))?(?:-(\d+))?$/", $str, $matches)) - throw new Exception(sprintf("GetAllStatesForDevice(): didn't match the regexp !!!: %s", $str)); - - $out[] = array( - 'uuid' => (isset($matches[1]) ? $matches[1] : false), - 'type' => (isset($matches[2]) ? $matches[2] : false), - 'counter' => (isset($matches[3]) ? $matches[3] : false), - ); - } - - return $out; - } - - - /** - * Return if the User-Device has permission to sync against this Z-Push. - * - * @param string $user Username - * @param string $devid DeviceId - * - * @access public - * @return integer - */ - public function GetUserDevicePermission($user, $devid) { - $mutex = new SimpleMutex(); - - $status = SYNC_COMMONSTATUS_SUCCESS; - - $userFile = STATE_DIR . 'PreAuthUserDevices'; - - if ($mutex->Block()) { - if (@file_exists($userFile)) { - $userList = json_decode(@file_get_contents($userFile), true); - } - else { - $userList = Array(); - } - - // Android PROVISIONING initial step - // LG-D802 is sending an empty deviceid - if ($devid != "validate" && $devid != "") { - $changed = false; - - if (array_key_exists($user, $userList)) { - // User already pre-authorized - - // User could be blocked if a "authorized" device exist and it's false - if (!$userList[$user]["authorized"]) { - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Blocked user '%s', tried '%s'", $user, $devid)); - } - else { - if (array_key_exists($devid, $userList[$user])) { - // Device pre-authorized found - - if ($userList[$user][$devid] === false) { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Blocked device '%s' for user '%s'", $devid, $user)); - } - else { - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Pre-authorized device '%s' for user '%s'", $devid, $user)); - } - } - else { - // Device not pre-authorized - - if (defined('PRE_AUTHORIZE_NEW_DEVICES') && PRE_AUTHORIZE_NEW_DEVICES === true) { - if (defined('PRE_AUTHORIZE_MAX_DEVICES') && PRE_AUTHORIZE_MAX_DEVICES >= count($userList[$user])) { - $userList[$user][$devid] = true; - $changed = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Pre-authorized new device '%s' for user '%s'", $devid, $user)); - } - else { - $status = SYNC_COMMONSTATUS_MAXDEVICESREACHED; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Max number of devices reached for user '%s', tried '%s'", $user, $devid)); - } - } - else { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - $userList[$user][$devid] = false; - $changed = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Blocked new device '%s' for user '%s'", $devid, $user)); - } - } - } - } - else { - // User not pre-authorized - - if (defined('PRE_AUTHORIZE_NEW_USERS') && PRE_AUTHORIZE_NEW_USERS === true) { - $userList[$user] = array("authorized" => true); - if (defined('PRE_AUTHORIZE_NEW_DEVICES') && PRE_AUTHORIZE_NEW_DEVICES === true) { - if (defined('PRE_AUTHORIZE_MAX_DEVICES') && PRE_AUTHORIZE_MAX_DEVICES >= count($userList[$user])) { - $userList[$user][$devid] = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Pre-authorized new device '%s' for new user '%s'", $devid, $user)); - } - } - else { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - $userList[$user][$devid] = false; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Blocked new device '%s' for new user '%s'", $devid, $user)); - } - - $changed = true; - } - else { - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - $userList[$user] = array("authorized" => false, $devid => false); - $changed = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("FileStateMachine->GetUserDevicePermission(): Blocked new user '%s' and device '%s'", $user, $devid)); - } - } - - if ($changed) { - file_put_contents($userFile, json_encode($userList)); - } - } - - $mutex->Release(); - } - - return $status; - } - - - /**---------------------------------------------------------------------------------------------------------- - * Private FileStateMachine stuff - */ - - /** - * Returns the full path incl. filename for a key (generally uuid) and a counter - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) default false - * @param boolean $doNotCreateDirs (opt) indicates if missing subdirectories should be created, default false - * - * @access private - * @return string - * @throws StateInvalidException - */ - private function getFullFilePath($devid, $type, $key = false, $counter = false, $doNotCreateDirs = false) { - $testkey = $devid . (($key !== false)? "-". $key : "") . (($type !== "")? "-". $type : ""); - if (preg_match('/^[a-zA-Z0-9-]+$/', $testkey, $matches) || ($type == "" && $key === false)) - $internkey = $testkey . (($counter && is_int($counter))?"-".$counter:""); - else - throw new StateInvalidException("FileStateMachine->getFullFilePath(): Invalid state deviceid, type, key or in any combination"); - - return $this->getDirectoryForDevice($devid, $doNotCreateDirs) ."/". $internkey; - } - - /** - * Checks if the configured path exists and if a subfolder structure is available - * A two level deep subdirectory structure is build to save the states. - * The subdirectories where to save, are determined with device id - * - * @param string $devid the device id - * @param boolen $doNotCreateDirs (opt) by default false - indicates if the subdirs should be created - * - * @access private - * @return string/boolean returns the full directory of false if the dirs can not be created - * @throws FatalMisconfigurationException when configured directory is not writeable - */ - private function getDirectoryForDevice($devid, $doNotCreateDirs = false) { - $firstLevel = substr(strtolower($devid), -1, 1); - $secondLevel = substr(strtolower($devid), -2, 1); - - $dir = STATE_DIR . $firstLevel . "/" . $secondLevel; - if (is_dir($dir)) - return $dir; - - if ($doNotCreateDirs === false) { - // try to create the subdirectory structure necessary - $fldir = STATE_DIR . $firstLevel; - if (!is_dir($fldir)) { - $dirOK = mkdir($fldir); - if (!$dirOK) - throw new FatalMisconfigurationException("FileStateMachine->getDirectoryForDevice(): Not possible to create state sub-directory: ". $fldir); - } - - if (!is_dir($dir)) { - $dirOK = mkdir($dir); - if (!$dirOK) - throw new FatalMisconfigurationException("FileStateMachine->getDirectoryForDevice(): Not possible to create state sub-directory: ". $dir); - } - else - return $dir; - } - return false; - } - - /** - * Retrieves the mapped username for a specific username and backend. - * - * @param string $username The username to lookup - * @param string $backend Name of the backend to lookup - * - * @return string The mapped username or null if none found - */ - public function GetMappedUsername($username, $backend) { - $mutex = new SimpleMutex(); - - // exclusive block - if ($mutex->Block()) { - // Read current mapping - $filecontents = @file_get_contents($this->usermapfilename); - if ($filecontents) - $mapping = unserialize($filecontents); - else - $mapping = array(); - $mutex->Release(); - } - - // Find mapping - $key = $username . '/' . $backend; - if (isset($mapping[$key])) { - return $mapping[$key]; - } - return null; - } - - /** - * Maps a username for a specific backend to another username. - * - * @param string $username The username to map - * @param string $backend Name of the backend - * @param string $mappedname The mappend username - * - * @return boolean - */ - public function MapUsername($username, $backend, $mappedname) { - $mutex = new SimpleMutex(); - - // exclusive block - if ($mutex->Block()) { - // Read current mapping - $filecontents = @file_get_contents($this->usermapfilename); - if ($filecontents) - $mapping = unserialize($filecontents); - else - $mapping = array(); - - // Map username + backend to the mapped username - $key = $username . '/' . $backend; - $mapping[$key] = $mappedname; - - // Write mapping file - $bytes = file_put_contents($this->usermapfilename, serialize($mapping)); - if ($bytes === false) { - ZLog::Write(LOGLEVEL_ERROR, "Unable to write to mapping file"); - return false; - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->MapUsername(): wrote %d bytes to mapping file", $bytes)); - - $mutex->Release(); - } - return true; - } - - /** - * Unmaps a username for a specific backend. - * - * @param string $username The username to unmap - * @param string $backend Name of the backend - * - * @return boolean - */ - public function UnmapUsername($username, $backend) { - $mutex = new SimpleMutex(); - - // exclusive block - if ($mutex->Block()) { - // Read current mapping - $filecontents = @file_get_contents($this->usermapfilename); - if ($filecontents) - $mapping = unserialize($filecontents); - else - $mapping = array(); - - // Unmap username + backend - $key = $username . '/' . $backend; - if (!isset($mapping[$key])) { - ZLog::Write(LOGLEVEL_INFO, "Username and backend not found in mapping file"); - return false; - } - unset($mapping[$key]); - - // Write mapping file - $bytes = file_put_contents($this->usermapfilename, serialize($mapping)); - if ($bytes === false) { - ZLog::Write(LOGLEVEL_ERROR, "Unable to write to mapping file"); - return false; - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("FileStateMachine->UnmapUsername(): wrote %d bytes to mapping file", $bytes)); - - $mutex->Release(); - } - return true; - } -} \ No newline at end of file diff --git a/sources/lib/default/searchprovider.php b/sources/lib/default/searchprovider.php deleted file mode 100644 index 8072e85..0000000 --- a/sources/lib/default/searchprovider.php +++ /dev/null @@ -1,124 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -/********************************************************************* - * The SearchProvider is a stub to implement own search funtionality - * - * If you wish to implement an alternative search method, you should implement the - * ISearchProvider interface like the BackendSearchLDAP backend - */ -class SearchProvider implements ISearchProvider{ - - /** - * Constructor - * initializes the searchprovider to perform the search - * - * @access public - * @return - * @throws StatusException, FatalException - */ - public function SearchProvider() { - } - - /** - * Indicates if a search type is supported by this SearchProvider - * Currently only the type ISearchProvider::SEARCH_GAL (Global Address List) is implemented - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype) { - return ($searchtype == ISearchProvider::SEARCH_GAL); - } - - /** - * Searches the GAL - * - * @param string $searchquery string to be searched for - * @param string $searchrange specified searchrange - * - * @access public - * @return array search results - * @throws StatusException - */ - public function GetGALSearchResults($searchquery, $searchrange) { - return array(); - } - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo){ - return array(); - } - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid) { - return true; - } - - /** - * Disconnects from the current search provider - * - * @access public - * @return boolean - */ - public function Disconnect() { - return true; - } -} diff --git a/sources/lib/default/simplemutex.php b/sources/lib/default/simplemutex.php deleted file mode 100644 index 5280109..0000000 --- a/sources/lib/default/simplemutex.php +++ /dev/null @@ -1,76 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SimpleMutex { - - private $file; - private $fp; - - public function __construct($file = __FILE__) { - $this->file = $file; - } - - /** - * Blocks the mutex - * Method blocks until mutex is available! - * ATTENTION: make sure that you *always* release a blocked mutex! - * - * @access public - * @return boolean - */ - public function Block() { - $this->fp = fopen($this->file, 'r'); - return flock($this->fp, LOCK_EX); - } - - /** - * Releases the mutex - * After the release other processes are able to block the mutex themselfs - * - * @access public - * @return boolean - */ - public function Release() { - return flock($this->fp, LOCK_UN) && fclose($this->fp) && $this->fp = null; - } -} diff --git a/sources/lib/default/sqlstatemachine.php b/sources/lib/default/sqlstatemachine.php deleted file mode 100644 index 1561b47..0000000 --- a/sources/lib/default/sqlstatemachine.php +++ /dev/null @@ -1,929 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SqlStateMachine implements IStateMachine { - const SUPPORTED_STATE_VERSION = IStateMachine::STATEVERSION_02; - const VERSION = "version"; - - - - private $dbh; - private $options; - - /** - * Constructor - * - * Performs some basic checks and initilizes the state directory - * - * @access public - * @throws FatalMisconfigurationException - */ - public function SqlStateMachine() { - ZLog::Write(LOGLEVEL_DEBUG, "SqlStateMachine(): init"); - - if (!defined('STATE_SQL_DSN') || !defined('STATE_SQL_USER') || !defined('STATE_SQL_PASSWORD')) { - throw new FatalMisconfigurationException("No configuration for the state sql database available."); - } - - $this->options = array(); - if (defined('STATE_SQL_OPTIONS')) { - $this->options = unserialize(STATE_SQL_OPTIONS); - } - - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - } - catch(PDOException $ex) { - throw new FatalMisconfigurationException(sprintf("Not possible to connect to the state database: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh); - } - - /** - * Gets a hash value indicating the latest dataset of the named - * state with a specified key and counter. - * If the state is changed between two calls of this method - * the returned hash should be different - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * - * @access public - * @return string - * @throws StateNotFoundException, StateInvalidException - */ - public function GetStateHash($devid, $type, $key = false, $counter = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetStateHash(): '%s', '%s', '%s', '%s'", $devid, $type, $key, $counter)); - - $sql = "select updated_at from zpush_states where device_id = :devid and state_type = :type and uuid = :key and counter = :counter"; - $params = $this->getParams($devid, $type, $key, $counter); - - $hash = null; - $sth = null; - $record = null; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if (!$record) { - $this->clearConnection($this->dbh, $sth, $record); - throw new StateNotFoundException(sprintf("SqlStateMachine->GetStateHash(): Could not locate state")); - } - else { - // datetime->format("U") returns EPOCH - $datetime = new DateTime($record["updated_at"]); - $hash = $datetime->format("U"); - } - } - catch(PDOException $ex) { - $this->clearConnection($this->dbh, $sth, $record); - throw new StateNotFoundException(sprintf("SqlStateMachine->GetStateHash(): Could not locate state: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetStateHash(): return '%s'", $hash)); - - return $hash; - } - - /** - * Gets a state for a specified key and counter. - * This method sould call IStateMachine->CleanStates() - * to remove older states (same key, previous counters) - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * @param string $cleanstates (opt) - * - * @access public - * @return mixed - * @throws StateNotFoundException, StateInvalidException - */ - public function GetState($devid, $type, $key = false, $counter = false, $cleanstates = true) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetState(): '%s', '%s', '%s', '%s', '%s'", $devid, $type, $key, $counter, $cleanstates)); - if ($counter && $cleanstates) - $this->CleanStates($devid, $type, $key, $counter); - - $sql = "select state_data from zpush_states where device_id = :devid and state_type = :type and uuid = :key and counter = :counter"; - $params = $this->getParams($devid, $type, $key, $counter); - - $data = null; - $sth = null; - $record = null; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if (!$record) { - $this->clearConnection($this->dbh, $sth, $record); - // throw an exception on all other states, but not FAILSAVE as it's most of the times not there by default - if ($type !== IStateMachine::FAILSAVE) { - throw new StateNotFoundException(sprintf("SqlStateMachine->GetState(): Could not locate state")); - } - } - else { - if (is_string($record["state_data"])) { - // MySQL-PDO returns a string for LOB objects - $data = unserialize($record["state_data"]); - } - else { - $data = unserialize(stream_get_contents($record["state_data"])); - } - } - } - catch(PDOException $ex) { - $this->clearConnection($this->dbh, $sth, $record); - throw new StateNotFoundException(sprintf("SqlStateMachine->GetState(): Could not locate state: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $data; - } - - /** - * Writes ta state to for a key and counter - * - * @param mixed $state - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param int $counter (opt) - * - * @access public - * @return boolean - * @throws StateInvalidException - */ - public function SetState($state, $devid, $type, $key = false, $counter = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->SetState(): '%s', '%s', '%s', '%s'", $devid, $type, $key, $counter)); - - $sql = "select device_id from zpush_states where device_id = :devid and state_type = :type and uuid = :key and counter = :counter"; - $params = $this->getParams($devid, $type, $key, $counter); - - $sth = null; - $record = null; - $bytes = 0; - - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if (!$record) { - // New record - $sql = "insert into zpush_states (device_id, state_type, uuid, counter, state_data, created_at, updated_at) values (:devid, :type, :key, :counter, :data, :created_at, :updated_at)"; - - $sth = $this->dbh->prepare($sql); - $sth->bindValue(":created_at", $this->getNow(), PDO::PARAM_STR); - } - else { - // Existing record, we update it - $sql = "update zpush_states set state_data = :data, updated_at = :updated_at where device_id = :devid and state_type = :type and uuid = :key and counter = :counter"; - - $sth = $this->dbh->prepare($sql); - } - - $sth->bindParam(":devid", $devid, PDO::PARAM_STR); - $sth->bindParam(":type", $type, PDO::PARAM_STR); - $sth->bindParam(":key", $key, PDO::PARAM_STR); - $sth->bindValue(":counter", ($counter === false ? -1 : $counter), PDO::PARAM_INT); - $sth->bindValue(":data", serialize($state), PDO::PARAM_LOB); - $sth->bindValue(":updated_at", $this->getNow(), PDO::PARAM_STR); - - if (!$sth->execute() ) { - $this->clearConnection($this->dbh, $sth); - throw new FatalMisconfigurationException(sprintf("SqlStateMachine->SetState(): Could not write state")); - } - else { - $bytes = strlen(serialize($state)); - } - } - catch(PDOException $ex) { - $this->clearConnection($this->dbh, $sth); - throw new FatalMisconfigurationException(sprintf("SqlStateMachine->SetState(): Could not write state: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $bytes; - } - - /** - * Cleans up all older states - * If called with a $counter, all states previous state counter can be removed - * If called without $counter, all keys (independently from the counter) can be removed - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key - * @param string $counter (opt) - * - * @access public - * @return - * @throws StateInvalidException - */ - public function CleanStates($devid, $type, $key, $counter = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->CleanStates(): '%s', '%s', '%s', '%s'", $devid, $type, $key, $counter)); - - - if ($counter === false) { - // Remove all the states. Counter are -1 or > 0, then deleting >= -1 deletes all - $sql = "delete from zpush_states where device_id = :devid and state_type = :type and uuid = :key and counter >= :counter"; - } - else { - $sql = "delete from zpush_states where device_id = :devid and state_type = :type and uuid = :key and counter < :counter"; - } - $params = $this->getParams($devid, $type, $key, $counter); - - $sth = null; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->CleanStates(): Error deleting states: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - } - - /** - * Links a user to a device - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean indicating if the user was added or not (existed already) - */ - public function LinkUserDevice($username, $devid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->LinkUserDevice(): '%s', '%s'", $username, $devid)); - - $sth = null; - $record = null; - $changed = false; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select username from zpush_users where username = :username and device_id = :devid"; - $params = array(":username" => $username, ":devid" => $devid); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if ($record) { - ZLog::Write(LOGLEVEL_DEBUG, "SqlStateMachine->LinkUserDevice(): nothing changed"); - } - else { - $sth = null; - $sql = "insert into zpush_users (username, device_id, created_at, updated_at) values (:username, :devid, :created_at, :updated_at)"; - $params[":created_at"] = $params[":updated_at"] = $this->getNow(); - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->LinkUserDevice(): Linked user-device: '%s' '%s'", $username, $devid)); - $changed = true; - } - else { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->LinkUserDevice(): Unable to link user-device")); - } - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->LinkUserDevice(): Error linking user-device: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $changed; - } - - /** - * Unlinks a device from a user - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean - */ - public function UnLinkUserDevice($username, $devid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->UnLinkUserDevice(): '%s', '%s'", $username, $devid)); - - $sth = null; - $changed = false; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "delete from zpush_users where username = :username and device_id = :devid"; - $params = array(":username" => $username, ":devid" => $devid); - - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->UnLinkUserDevice(): Unlinked user-device: '%s' '%s'", $username, $devid)); - $changed = true; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, "SqlStateMachine->UnLinkUserDevice(): nothing changed"); - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->UnLinkUserDevice(): Error unlinking user-device: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth); - - return $changed; - } - - /** - * Get all UserDevice mapping - * - * @access public - * @return array - */ - public function GetAllUserDevice() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetAllUserDevice(): '%s'", $username)); - - $sth = null; - $record = null; - $out = array(); - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select device_id, username from zpush_users order by username"; - $sth = $this->dbh->prepare($sql); - $sth->execute(); - - while ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - if (!array_key_exists($record["username"], $out)) { - $out[$record["username"]] = array(); - } - $out[$record["username"]][] = $record["device_id"]; - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetAllUserDevice(): Error listing devices: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $out; - } - - /** - * Returns an array with all device ids for a user. - * If no user is set, all device ids should be returned - * - * @param string $username (opt) - * - * @access public - * @return array - */ - public function GetAllDevices($username = false) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetAllDevices(): '%s'", $username)); - - $sth = null; - $record = null; - $out = array(); - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - if ($username === false) { - $sql = "select distinct(device_id) from zpush_users order by device_id"; - $params = array(); - } - else { - $sql = "select device_id from zpush_users where username = :username order by device_id"; - $params = array(":username" => $username); - } - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - while ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $out[] = $record["device_id"]; - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetAllDevices(): Error listing devices: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $out; - } - - /** - * Returns the current version of the state files - * - * @access public - * @return int - */ - public function GetStateVersion() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetStateVersion()")); - - $sth = null; - $record = null; - $version = IStateMachine::STATEVERSION_01; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select key_value from zpush_settings where key_name = :key_name"; - $params = array(":key_name" => self::VERSION); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if ($record) { - $version = $record["key_value"]; - } - else { - $this->SetStateVersion(self::SUPPORTED_STATE_VERSION); - $version = self::SUPPORTED_STATE_VERSION; - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetStateVersion(): Error getting state version: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $version; - } - - /** - * Sets the current version of the state files - * - * @param int $version the new supported version - * - * @access public - * @return boolean - */ - public function SetStateVersion($version) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->SetStateVersion(): '%s'", $version)); - - $sth = null; - $record = null; - $status = false; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select key_value from zpush_settings where key_name = :key_name"; - $params = array(":key_name" => self::VERSION); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - $record = $sth->fetch(PDO::FETCH_ASSOC); - if ($record) { - $sth = null; - $sql = "update zpush_settings set key_value = :value, updated_at = :updated_at where key_name = :key_name"; - $params[":value"] = $version; - $params[":updated_at"] = $this->getNow(); - - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params)) { - $status = true; - } - } - else { - $sth = null; - $sql = "insert into zpush_settings (key_name, key_value, created_at, updated_at) values (:key_name, :value, :created_at, :updated_at)"; - $params[":value"] = $version; - $params[":updated_at"] = $params[":created_at"] = $this->getNow(); - - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params)) { - $status = true; - } - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->SetStateVersion(): Error saving state version: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $status; - } - - /** - * Returns all available states for a device id - * - * @param string $devid the device id - * - * @access public - * @return array(mixed) - */ - public function GetAllStatesForDevice($devid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetAllStatesForDevice(): '%s'", $devid)); - - $sth = null; - $record = null; - $out = array(); - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select state_type, uuid, counter from zpush_states where device_id = :devid order by id_state"; - $params = array(":devid" => $devid); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - - while ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $state = array('type' => false, 'counter' => false, 'uuid' => false); - if ($record["state_type"] !== null && strlen($record["state_type"]) > 0) { - $state["type"] = $record["state_type"]; - } - else { - if ($record["counter"] !== null && is_numeric($record["counter"])) { - $state["type"] = ""; - } - } - if ($record["counter"] !== null && strlen($record["counter"]) > 0) { - $state["counter"] = $record["counter"]; - } - if ($record["uuid"] !== null && strlen($record["uuid"]) > 0) { - $state["uuid"] = $record["uuid"]; - } - $out[] = $state; - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetAllStatesForDevice(): Error listing states: %s", $ex->getMessage())); - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $out; - } - - /** - * Return if the User-Device has permission to sync against this Z-Push. - * - * @param string $user Username - * @param string $devid DeviceId - * - * @access public - * @return integer - */ - public function GetUserDevicePermission($user, $devid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetUserDevicePermission('%s', '%s')", $user, $devid)); - - $status = SYNC_COMMONSTATUS_SUCCESS; - - $userExist = false; - $userBlocked = false; - $deviceExist = false; - $deviceBlocked = false; - - // Android PROVISIONING initial step - // LG-D802 is sending an empty deviceid - if ($devid != "validate" && $devid != "") { - - $sth = null; - $record = null; - try { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "select count(*) as pcount from zpush_preauth_users where username = :user and device_id != 'authorized' and authorized = 1"; - $params = array(":user" => $user); - - // Get number of authorized devices for user - $num_devid_user = 0; - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - if ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $num_devid_user = $record["pcount"]; - } - $record = null; - $sth = null; - - $sql = "select authorized from zpush_preauth_users where username = :user and device_id = :devid"; - $params = array(":user" => $user, ":devid" => "authorized"); - $paramsNewDevid = array(); - $paramsNewUser = array(); - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - if ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $userExist = true; - $userBlocked = !$record["authorized"]; - } - $record = null; - $sth = null; - - if ($userExist) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetUserDevicePermission(): User '%s', already pre-authorized", $user)); - - // User could be blocked if a "authorized" device exist and it's false - if ($userBlocked) { - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Blocked user '%s', tried '%s'", $user, $devid)); - } - else { - $params[":devid"] = $devid; - - $sth = $this->dbh->prepare($sql); - $sth->execute($params); - if ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $deviceExist = true; - $deviceBlocked = !$record["authorized"]; - } - $record = null; - $sth = null; - - if ($deviceExist) { - // Device pre-authorized found - - if ($deviceBlocked) { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Blocked device '%s' for user '%s'", $devid, $user)); - } - else { - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Pre-authorized device '%s' for user '%s'", $devid, $user)); - } - } - else { - // Device not pre-authorized - - if (defined('PRE_AUTHORIZE_NEW_DEVICES') && PRE_AUTHORIZE_NEW_DEVICES === true) { - if (defined('PRE_AUTHORIZE_MAX_DEVICES') && PRE_AUTHORIZE_MAX_DEVICES > $num_devid_user) { - $paramsNewDevid[":auth"] = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Pre-authorized new device '%s' for user '%s'", $devid, $user)); - } - else { - $status = SYNC_COMMONSTATUS_MAXDEVICESREACHED; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Max number of devices reached for user '%s', tried '%s'", $user, $devid)); - } - } - else { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - $paramsNewDevid[":auth"] = false; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Blocked new device '%s' for user '%s'", $devid, $user)); - } - } - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetUserDevicePermission(): User '%s', not pre-authorized", $user)); - - if (defined('PRE_AUTHORIZE_NEW_USERS') && PRE_AUTHORIZE_NEW_USERS === true) { - $paramsNewUser[":auth"] = true; - if (defined('PRE_AUTHORIZE_NEW_DEVICES') && PRE_AUTHORIZE_NEW_DEVICES === true) { - if (defined('PRE_AUTHORIZE_MAX_DEVICES') && PRE_AUTHORIZE_MAX_DEVICES > $num_devid_user) { - $paramsNewDevid[":auth"] = true; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Pre-authorized new device '%s' for new user '%s'", $devid, $user)); - } - else { - $status = SYNC_COMMONSTATUS_MAXDEVICESREACHED; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Max number of devices reached for user '%s', tried '%s'", $user, $devid)); - } - } - else { - $status = SYNC_COMMONSTATUS_DEVICEBLOCKEDFORUSER; - $paramsNewDevid[":auth"] = false; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Blocked new device '%s' for new user '%s'", $devid, $user)); - } - } - else { - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - $paramsNewUser[":auth"] = false; - $paramsNewDevid[":auth"] = false; - ZLog::Write(LOGLEVEL_INFO, sprintf("SqlStateMachine->GetUserDevicePermission(): Blocked new user '%s' and device '%s'", $user, $devid)); - } - } - - if (count($paramsNewUser) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetUserDevicePermission(): Creating new user '%s'", $user)); - - $sql = "insert into zpush_preauth_users (username, device_id, authorized, created_at, updated_at) values (:user, :devid, :auth, :created_at, :updated_at)"; - $paramsNewUser[":user"] = $user; - $paramsNewUser[":devid"] = "authorized"; - $paramsNewUser[":created_at"] = $paramsNewUser[":updated_at"] = $this->getNow(); - - $sth = $this->dbh->prepare($sql); - if (!$sth->execute($paramsNewUser)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetUserDevicePermission(): Error creating new user")); - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - } - } - - if (count($paramsNewDevid) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SqlStateMachine->GetUserDevicePermission(): Creating new device '%s' for user '%s'", $devid, $user)); - - $sql = "insert into zpush_preauth_users (username, device_id, authorized, created_at, updated_at) values (:user, :devid, :auth, :created_at, :updated_at)"; - $paramsNewDevid[":user"] = $user; - $paramsNewDevid[":devid"] = $devid; - $paramsNewDevid[":created_at"] = $paramsNewDevid[":updated_at"] = $this->getNow(); - - $sth = $this->dbh->prepare($sql); - if (!$sth->execute($paramsNewDevid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetUserDevicePermission(): Error creating user new device")); - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - } - } - } - catch(PDOException $ex) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetUserDevicePermission(): Error checking permission for username '%s' device '%s': %s", $user, $devid, $ex->getMessage())); - $status = SYNC_COMMONSTATUS_USERDISABLEDFORSYNC; - } - - $this->clearConnection($this->dbh, $sth, $record); - } - - return $status; - } - - /** - * Retrieves the mapped username for a specific username and backend. - * - * @param string $username The username to lookup - * @param string $backend Name of the backend to lookup - * - * @return string The mapped username or null if none found - */ - public function GetMappedUsername($username, $backend) { - $result = null; - - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "SELECT `mappedname` FROM `zpush_combined_usermap` WHERE `username` = :user AND `backend` = :backend"; - $params = array("user" => $username, "backend" => $backend); - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params) === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->GetMappedUsername(): Failed to execute query")); - } else if ($record = $sth->fetch(PDO::FETCH_ASSOC)) { - $result = $record["mappedname"]; - } - - $this->clearConnection($this->dbh, $sth, $record); - - return $result; - } - - /** - * Maps a username for a specific backend to another username. - * - * @param string $username The username to map - * @param string $backend Name of the backend - * @param string $mappedname The mappend username - * - * @return boolean - */ - public function MapUsername($username, $backend, $mappedname) { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = " - INSERT INTO `zpush_combined_usermap` (`username`, `backend`, `mappedname`, `created_at`, `updated_at`) - VALUES (:user, :backend, :mappedname, NOW(), NOW()) - ON DUPLICATE KEY UPDATE `mappedname` = :mappedname2, `updated_at` = NOW() - "; - $params = array("user" => $username, "backend" => $backend, "mappedname" => $mappedname, "mappedname2" => $mappedname); - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params) === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->MapUsername(): Failed to execute query")); - return false; - } - - $this->clearConnection($this->dbh, $sth); - return true; - } - - /** - * Unmaps a username for a specific backend. - * - * @param string $username The username to unmap - * @param string $backend Name of the backend - * - * @return boolean - */ - public function UnmapUsername($username, $backend) { - $this->dbh = new PDO(STATE_SQL_DSN, STATE_SQL_USER, STATE_SQL_PASSWORD, $this->options); - - $sql = "DELETE FROM `zpush_combined_usermap` WHERE `username` = :user AND `backend` = :backend"; - $params = array("user" => $username, "backend" => $backend); - $sth = $this->dbh->prepare($sql); - if ($sth->execute($params) === false) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->UnmapUsername(): Failed to execute query")); - return false; - } else if ($sth->rowCount() !== 1) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SqlStateMachine->MapUsername(): Invalid mapping of username and backend")); - return false; - } - - $this->clearConnection($this->dbh, $sth); - return true; - } - - /**---------------------------------------------------------------------------------------------------------- - * Private SqlStateMachine stuff - */ - - /** - * Return a string with the datetime NOW - * - * @return string - * @access private - */ - private function getNow() { - $now = new DateTime("NOW"); - return $now->format("Y-m-d H:i:s"); - } - - /** - * Return an array with the params for the PDO query - * - * @params string $devid - * @params string $type - * @params string $key - * @params string $counter - * @return array - * @access private - */ - private function getParams($devid, $type, $key, $counter) { - return array(":devid" => $devid, ":type" => $type, ":key" => $key, ":counter" => ($counter === false ? -1 : $counter) ); - } - - /** - * Free PDO resources. - * - * @params PDOConnection $dbh - * @params PDOStatement $sth - * @params PDORecord $record - * @access private - */ - private function clearConnection(&$dbh, &$sth = null, &$record = null) { - if ($record != null) { - $record = null; - } - if ($sth != null) { - $sth = null; - } - if ($dbh != null) { - $dbh = null; - } - } - -} \ No newline at end of file diff --git a/sources/lib/exceptions/authenticationrequiredexception.php b/sources/lib/exceptions/authenticationrequiredexception.php deleted file mode 100644 index f66509c..0000000 --- a/sources/lib/exceptions/authenticationrequiredexception.php +++ /dev/null @@ -1,50 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class AuthenticationRequiredException extends HTTPReturnCodeException { - protected $defaultLogLevel = LOGLEVEL_INFO; - protected $httpReturnCode = HTTP_CODE_401; - protected $httpReturnMessage = "Unauthorized"; - protected $httpHeaders = array('WWW-Authenticate: Basic realm="ZPush"'); - protected $showLegal = true; -} diff --git a/sources/lib/exceptions/fatalexception.php b/sources/lib/exceptions/fatalexception.php deleted file mode 100644 index 8a5f661..0000000 --- a/sources/lib/exceptions/fatalexception.php +++ /dev/null @@ -1,45 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FatalException extends ZPushException {} diff --git a/sources/lib/exceptions/fatalmisconfigurationexception.php b/sources/lib/exceptions/fatalmisconfigurationexception.php deleted file mode 100644 index 93b876a..0000000 --- a/sources/lib/exceptions/fatalmisconfigurationexception.php +++ /dev/null @@ -1,44 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FatalMisconfigurationException extends FatalException {} diff --git a/sources/lib/exceptions/fatalnotimplementedexception.php b/sources/lib/exceptions/fatalnotimplementedexception.php deleted file mode 100644 index cb49f9d..0000000 --- a/sources/lib/exceptions/fatalnotimplementedexception.php +++ /dev/null @@ -1,45 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FatalNotImplementedException extends FatalException {} diff --git a/sources/lib/exceptions/httpreturncodeexception.php b/sources/lib/exceptions/httpreturncodeexception.php deleted file mode 100644 index 80fa85f..0000000 --- a/sources/lib/exceptions/httpreturncodeexception.php +++ /dev/null @@ -1,54 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class HTTPReturnCodeException extends FatalException { - protected $defaultLogLevel = LOGLEVEL_ERROR; - protected $showLegal = false; - - public function HTTPReturnCodeException($message = "", $code = 0, $previous = NULL, $logLevel = false) { - if ($code) - $this->httpReturnCode = $code; - parent::__construct($message, (int) $code, $previous, $logLevel); - } -} diff --git a/sources/lib/exceptions/nohierarchycacheavailableexception.php b/sources/lib/exceptions/nohierarchycacheavailableexception.php deleted file mode 100644 index 5a6f1be..0000000 --- a/sources/lib/exceptions/nohierarchycacheavailableexception.php +++ /dev/null @@ -1,44 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class NoHierarchyCacheAvailableException extends StateNotFoundException {} diff --git a/sources/lib/exceptions/nopostrequestexception.php b/sources/lib/exceptions/nopostrequestexception.php deleted file mode 100644 index 08eb06a..0000000 --- a/sources/lib/exceptions/nopostrequestexception.php +++ /dev/null @@ -1,49 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class NoPostRequestException extends FatalException { - const OPTIONS_REQUEST = 1; - const GET_REQUEST = 2; - protected $defaultLogLevel = LOGLEVEL_DEBUG; -} diff --git a/sources/lib/exceptions/notimplementedexception.php b/sources/lib/exceptions/notimplementedexception.php deleted file mode 100644 index 36fee4b..0000000 --- a/sources/lib/exceptions/notimplementedexception.php +++ /dev/null @@ -1,47 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class NotImplementedException extends ZPushException { - protected $defaultLogLevel = LOGLEVEL_ERROR; -} diff --git a/sources/lib/exceptions/provisioningrequiredexception.php b/sources/lib/exceptions/provisioningrequiredexception.php deleted file mode 100644 index b861ef9..0000000 --- a/sources/lib/exceptions/provisioningrequiredexception.php +++ /dev/null @@ -1,49 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ProvisioningRequiredException extends HTTPReturnCodeException { - protected $defaultLogLevel = LOGLEVEL_INFO; - protected $httpReturnCode = HTTP_CODE_449; - protected $httpReturnMessage = "Retry after sending a PROVISION command"; -} diff --git a/sources/lib/exceptions/stateinvalidexception.php b/sources/lib/exceptions/stateinvalidexception.php deleted file mode 100644 index b410aad..0000000 --- a/sources/lib/exceptions/stateinvalidexception.php +++ /dev/null @@ -1,44 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StateInvalidException extends StatusException {} diff --git a/sources/lib/exceptions/statenotfoundexception.php b/sources/lib/exceptions/statenotfoundexception.php deleted file mode 100644 index f75e09d..0000000 --- a/sources/lib/exceptions/statenotfoundexception.php +++ /dev/null @@ -1,45 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StateNotFoundException extends StatusException {} diff --git a/sources/lib/exceptions/statenotyetavailableexception.php b/sources/lib/exceptions/statenotyetavailableexception.php deleted file mode 100644 index 998fd66..0000000 --- a/sources/lib/exceptions/statenotyetavailableexception.php +++ /dev/null @@ -1,44 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StateNotYetAvailableException extends StatusException {} diff --git a/sources/lib/exceptions/statusexception.php b/sources/lib/exceptions/statusexception.php deleted file mode 100644 index 164f6e1..0000000 --- a/sources/lib/exceptions/statusexception.php +++ /dev/null @@ -1,46 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StatusException extends ZPushException { - protected $defaultLogLevel = LOGLEVEL_INFO; -} diff --git a/sources/lib/exceptions/syncobjectbrokenexception.php b/sources/lib/exceptions/syncobjectbrokenexception.php deleted file mode 100644 index 2072a0f..0000000 --- a/sources/lib/exceptions/syncobjectbrokenexception.php +++ /dev/null @@ -1,71 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncObjectBrokenException extends ZPushException { - protected $defaultLogLevel = LOGLEVEL_WARN; - private $syncObject; - - /** - * Returns the SyncObject which caused this Exception (if set) - * - * @access public - * @return SyncObject - */ - public function GetSyncObject() { - return isset($this->syncObject) ? $this->syncObject : false; - } - - /** - * Sets the SyncObject which caused the exception so it can be later retrieved - * - * @param SyncObject $syncobject - * - * @access public - * @return boolean - */ - public function SetSyncObject($syncobject) { - $this->syncObject = $syncobject; - return true; - } -} diff --git a/sources/lib/exceptions/wbxmlexception.php b/sources/lib/exceptions/wbxmlexception.php deleted file mode 100644 index 11808d4..0000000 --- a/sources/lib/exceptions/wbxmlexception.php +++ /dev/null @@ -1,44 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class WBXMLException extends FatalNotImplementedException {} diff --git a/sources/lib/exceptions/zpushexception.php b/sources/lib/exceptions/zpushexception.php deleted file mode 100644 index 6832cd9..0000000 --- a/sources/lib/exceptions/zpushexception.php +++ /dev/null @@ -1,73 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ZPushException extends Exception { - protected $defaultLogLevel = LOGLEVEL_FATAL; - protected $httpReturnCode = HTTP_CODE_500; - protected $httpReturnMessage = "Internal Server Error"; - protected $httpHeaders = array(); - protected $showLegal = true; - - public function ZPushException($message = "", $code = 0, $previous = NULL, $logLevel = false) { - if (! $message) - $message = $this->httpReturnMessage; - - if (!$logLevel) - $logLevel = $this->defaultLogLevel; - - ZLog::Write($logLevel, sprintf("%s: %s - code: %s - file: %s:%s", get_class($this), $message, $code, $this->getFile(), $this->getLine()), false); - parent::__construct($message, (int) $code); - } - - public function getHTTPCodeString() { - return $this->httpReturnCode . " ". $this->httpReturnMessage; - } - - public function getHTTPHeaders() { - return $this->httpHeaders; - } - - public function showLegalNotice() { - return $this->showLegal; - } -} \ No newline at end of file diff --git a/sources/lib/interface/ibackend.php b/sources/lib/interface/ibackend.php deleted file mode 100644 index 74b069c..0000000 --- a/sources/lib/interface/ibackend.php +++ /dev/null @@ -1,310 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -interface IBackend { - /** - * Returns a IStateMachine implementation used to save states - * - * @access public - * @return boolean/object if false is returned, the default Statemachine is - * used else the implementation of IStateMachine - */ - public function GetStateMachine(); - - /** - * Returns a ISearchProvider implementation used for searches - * - * @access public - * @return object Implementation of ISearchProvider - */ - public function GetSearchProvider(); - - /** - * Indicates which AS version is supported by the backend. - * Depending on this value the supported AS version announced to the - * mobile device is set. - * - * @access public - * @return string AS version constant - */ - public function GetSupportedASVersion(); - - /** - * Authenticates the user - * - * @param string $username - * @param string $domain - * @param string $password - * - * @access public - * @return boolean - * @throws FatalException e.g. some required libraries are unavailable - */ - public function Logon($username, $domain, $password); - - /** - * Setup the backend to work on a specific store or checks ACLs there. - * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be - * performed on this store (switch operations store). - * If the ACL check is enabled, this operation should just indicate the ACL status on - * the submitted store, without changing the store for operations. - * For the ACL status, the currently logged on user MUST have access rights on - * - the entire store - admin access if no folderid is sent, or - * - on a specific folderid in the store (secretary/full access rights) - * - * The ACLcheck MUST fail if a folder of the authenticated user is checked! - * - * @param string $store target store, could contain a "domain\user" value - * @param boolean $checkACLonly if set to true, Setup() should just check ACLs - * @param string $folderid if set, only ACLs on this folderid are relevant - * - * @access public - * @return boolean - */ - public function Setup($store, $checkACLonly = false, $folderid = false); - - /** - * Logs off - * non critical operations closing the session should be done here - * - * @access public - * @return boolean - */ - public function Logoff(); - - /** - * Returns an array of SyncFolder types with the entire folder hierarchy - * on the server (the array itself is flat, but refers to parents via the 'parent' property - * - * provides AS 1.0 compatibility - * - * @access public - * @return array SYNC_FOLDER - */ - public function GetHierarchy(); - - /** - * Returns the importer to process changes from the mobile - * If no $folderid is given, hierarchy data will be imported - * With a $folderid a content data will be imported - * - * @param string $folderid (opt) - * - * @access public - * @return object implements IImportChanges - * @throws StatusException - */ - public function GetImporter($folderid = false); - - /** - * Returns the exporter to send changes to the mobile - * If no $folderid is given, hierarchy data should be exported - * With a $folderid a content data is expected - * - * @param string $folderid (opt) - * - * @access public - * @return object implements IExportChanges - * @throws StatusException - */ - public function GetExporter($folderid = false); - - /** - * Sends an e-mail - * This messages needs to be saved into the 'sent items' folder - * - * Basically two things can be done - * 1) Send the message to an SMTP server as-is - * 2) Parse the message, and send it some other way - * - * @param SyncSendMail $sm SyncSendMail object - * - * @access public - * @return boolean - * @throws StatusException - */ - public function SendMail($sm); - - /** - * Returns all available data of a single message - * - * @param string $folderid - * @param string $id - * @param ContentParameters $contentparameters flag - * - * @access public - * @return object(SyncObject) - * @throws StatusException - */ - public function Fetch($folderid, $id, $contentparameters); - - /** - * Returns the waste basket - * - * The waste basked is used when deleting items; if this function returns a valid folder ID, - * then all deletes are handled as moves and are sent to the backend as a move. - * If it returns FALSE, then deletes are handled as real deletes - * - * @access public - * @return string - */ - public function GetWasteBasket(); - - /** - * Returns the content of the named attachment as stream. The passed attachment identifier is - * the exact string that is returned in the 'AttName' property of an SyncAttachment. - * Any information necessary to locate the attachment must be encoded in that 'attname' property. - * Data is written directly - 'print $data;' - * - * @param string $attname - * - * @access public - * @return SyncItemOperationsAttachment - * @throws StatusException - */ - public function GetAttachmentData($attname); - - /** - * Deletes all contents of the specified folder. - * This is generally used to empty the trash (wastebasked), but could also be used on any - * other folder. - * - * @param string $folderid - * @param boolean $includeSubfolders (opt) also delete sub folders, default true - * - * @access public - * @return boolean - * @throws StatusException - */ - public function EmptyFolder($folderid, $includeSubfolders = true); - - /** - * Processes a response to a meeting request. - * CalendarID is a reference and has to be set if a new calendar item is created - * - * @param string $requestid id of the object containing the request - * @param string $folderid id of the parent folder of $requestid - * @param string $response - * - * @access public - * @return string id of the created/updated calendar obj - * @throws StatusException - */ - public function MeetingResponse($requestid, $folderid, $response); - - /** - * Indicates if the backend has a ChangesSink. - * A sink is an active notification mechanism which does not need polling. - * - * @access public - * @return boolean - */ - public function HasChangesSink(); - - /** - * The folder should be considered by the sink. - * Folders which were not initialized should not result in a notification - * of IBacken->ChangesSink(). - * - * @param string $folderid - * - * @access public - * @return boolean false if there is any problem with that folder - */ - public function ChangesSinkInitialize($folderid); - - /** - * The actual ChangesSink. - * For max. the $timeout value this method should block and if no changes - * are available return an empty array. - * If changes are available a list of folderids is expected. - * - * @param int $timeout max. amount of seconds to block - * - * @access public - * @return array - */ - public function ChangesSink($timeout = 30); - - /** - * Applies settings to and gets informations from the device - * - * @param SyncObject $settings (SyncOOF or SyncUserInformation possible) - * - * @access public - * @return SyncObject $settings - */ - public function Settings($settings); - - /** - * Resolves recipients - * - * @param SyncObject $resolveRecipients - * - * @access public - * @return SyncObject $resolveRecipients - */ - public function ResolveRecipients($resolveRecipients); - - /** - * Returns the email address and the display name of the user. Used by autodiscover. - * - * @param string $username The username - * - * @access public - * @return Array - */ - public function GetUserDetails($username); - - /** - * Returns the username of the currently active user - * - * @access public - * @return Array - */ - public function GetCurrentUsername(); -} diff --git a/sources/lib/interface/ichanges.php b/sources/lib/interface/ichanges.php deleted file mode 100644 index 9087bb6..0000000 --- a/sources/lib/interface/ichanges.php +++ /dev/null @@ -1,84 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -interface IChanges { - /** - * Constructor - * - * @throws StatusException - */ - - /** - * Initializes the state and flags - * - * @param string $state - * @param int $flags - * - * @access public - * @return boolean status flag - * @throws StatusException - */ - public function Config($state, $flags = 0); - - /** - * Configures additional parameters used for content synchronization - * - * @param ContentParameters $contentparameters - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ConfigContentParameters($contentparameters); - - /** - * Reads and returns the current state - * - * @access public - * @return string - */ - public function GetState(); -} diff --git a/sources/lib/interface/iexportchanges.php b/sources/lib/interface/iexportchanges.php deleted file mode 100644 index 51a6202..0000000 --- a/sources/lib/interface/iexportchanges.php +++ /dev/null @@ -1,74 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -interface IExportChanges extends IChanges { - /** - * Sets the importer where the exporter will sent its changes to - * This exporter should also be ready to accept calls after this - * - * @param object &$importer Implementation of IImportChanges - * - * @access public - * @return boolean - * @throws StatusException - */ - public function InitializeExporter(&$importer); - - /** - * Returns the amount of changes to be exported - * - * @access public - * @return int - */ - public function GetChangeCount(); - - /** - * Synchronizes a change to the configured importer - * - * @access public - * @return array with status information - */ - public function Synchronize(); -} diff --git a/sources/lib/interface/iimportchanges.php b/sources/lib/interface/iimportchanges.php deleted file mode 100644 index 321c56b..0000000 --- a/sources/lib/interface/iimportchanges.php +++ /dev/null @@ -1,141 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -interface IImportChanges extends IChanges { - - /**---------------------------------------------------------------------------------------------------------- - * Methods for to import contents - */ - - /** - * Loads objects which are expected to be exported with the state - * Before importing/saving the actual message from the mobile, a conflict detection should be done - * - * @param ContentParameters $contentparameters - * @param string $state - * - * @access public - * @return boolean - * @throws StatusException - */ - public function LoadConflicts($contentparameters, $state); - - /** - * Imports a single message - * - * @param string $id - * @param SyncObject $message - * - * @access public - * @return boolean/string failure / id of message - * @throws StatusException - */ - public function ImportMessageChange($id, $message); - - /** - * Imports a deletion. This may conflict if the local object has been modified - * - * @param string $id - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageDeletion($id); - - /** - * Imports a change in 'read' flag - * This can never conflict - * - * @param string $id - * @param int $flags - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageReadFlag($id, $flags); - - /** - * Imports a move of a message. This occurs when a user moves an item to another folder - * - * @param string $id - * @param string $newfolder destination folder - * - * @access public - * @return boolean - * @throws StatusException - */ - public function ImportMessageMove($id, $newfolder); - - - /**---------------------------------------------------------------------------------------------------------- - * Methods to import hierarchy - */ - - /** - * Imports a change on a folder - * - * @param object $folder SyncFolder - * - * @access public - * @return boolean/string status/id of the folder - * @throws StatusException - */ - public function ImportFolderChange($folder); - - /** - * Imports a folder deletion - * - * @param string $id - * @param string $parent id - * - * @access public - * @return boolean/int success/SYNC_FOLDERHIERARCHY_STATUS - * @throws StatusException - */ - public function ImportFolderDeletion($id, $parent = false); - -} diff --git a/sources/lib/interface/iloopdetection.php b/sources/lib/interface/iloopdetection.php deleted file mode 100644 index 3a7fc05..0000000 --- a/sources/lib/interface/iloopdetection.php +++ /dev/null @@ -1,221 +0,0 @@ -FolderSync->Sync->FolderSync => ReSync) - * Ticket: https://jira.zarafa.com/browse/ZP-5 - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionIsHierarchyResyncRequired(); - - /** - * Indicates if a previous process could not be terminated - * - * Checks if there is an end time for the last entry on the stack - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionPreviousConnectionFailed(); - - /** - * Gets the PID of an outdated search process - * - * Returns false if there isn't any process - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionGetOutdatedSearchPID(); - - /** - * TRACKING OF BROKEN MESSAGES - * if a previousily ignored message is streamed again to the device it's tracked here - * - * There are two outcomes: - * - next uuid counter is higher than current -> message is fixed and successfully synchronized - * - next uuid counter is the same or uuid changed -> message is still broken - */ - - /** - * Adds a message to the tracking of broken messages - * Being tracked means that a broken message was streamed to the device. - * We save the latest uuid and counter so if on the next sync the counter is higher - * the message was accepted by the device. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return boolean - */ - public function SetBrokenMessage($folderid, $id); - - /** - * Gets a list of all ids of a folder which were tracked and which were - * accepted by the device from the last sync. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return array - */ - public function GetSyncedButBeforeIgnoredMessages($folderid); - - /** - * Marks a SyncState as "already used", e.g. when an import process started. - * This is most critical for DiffBackends, as an imported message would be exported again - * in the heartbeat if the notification is triggered before the import is complete. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean - */ - public function SetSyncStateUsage($folderid, $uuid, $counter); - - /** - * Checks if the given counter for a certain uuid+folderid was exported before. - * Returns also true if the counter are the same but previously there were - * changes to be exported. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean indicating if an uuid+counter were exported (with changes) before - */ - public function IsSyncStateObsolete($folderid, $uuid, $counter); - - /** - * MESSAGE LOOP DETECTION - */ - - /** - * Loop detection mechanism - * - * 1. request counter is higher than the previous counter (somehow default) - * 1.1) standard situation -> do nothing - * 1.2) loop information exists - * 1.2.1) request counter < maxCounter AND no ignored data -> continue in loop mode - * 1.2.2) request counter < maxCounter AND ignored data -> we have already encountered issue, return to normal - * - * 2. request counter is the same as the previous, but no data was sent on the last request (standard situation) - * - * 3. request counter is the same as the previous and last time objects were sent (loop!) - * 3.1) no loop was detected before, entereing loop mode -> save loop data, loopcount = 1 - * 3.2) loop was detected before, but are gone -> loop resolved - * 3.3) loop was detected before, continuing in loop mode -> this is probably the broken element,loopcount++, - * 3.3.1) item identified, loopcount >= 3 -> ignore item, set ignoredata flag - * - * @param string $folderid the current folder id to be worked on - * @param string $type the type of that folder (Email, Calendar, Contact, Task) - * @param string $uuid the synkkey - * @param string $counter the synckey counter - * @param string $maxItems the current amount of items to be sent to the mobile - * @param string $queuedMessages the amount of messages which were found by the exporter - * - * @access public - * @return boolean when returning true if a loop has been identified - */ - public function Detect($folderid, $type, $uuid, $counter, $maxItems, $queuedMessages); - - /** - * Indicates if the next messages should be ignored (not be sent to the mobile!) - * - * @param string $messageid (opt) id of the message which is to be exported next - * @param string $folderid (opt) parent id of the message - * @param boolean $markAsIgnored (opt) to peek without setting the next message to be - * ignored, set this value to false - * @access public - * @return boolean - */ - public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false); - - /** - * Clears loop detection data - * - * @param string $user (opt) user which data should be removed - user can not be specified without - * @param string $devid (opt) device id which data to be removed - * - * @return boolean - * @access public - */ - public function ClearData($user = false, $devid = false); - - /** - * Returns loop detection data for a user and device - * - * @param string $user - * @param string $devid - * - * @return array/boolean returns false if data not available - * @access public - */ - public function GetCachedData($user, $devid); -} diff --git a/sources/lib/interface/ipingtracking.php b/sources/lib/interface/ipingtracking.php deleted file mode 100644 index 69086a9..0000000 --- a/sources/lib/interface/ipingtracking.php +++ /dev/null @@ -1,12 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -interface ISearchProvider { - const SEARCH_GAL = "GAL"; - const SEARCH_MAILBOX = "MAILBOX"; - const SEARCH_DOCUMENTLIBRARY = "DOCUMENTLIBRARY"; - - /** - * Constructor - * - * @throws StatusException, FatalException - */ - - /** - * Indicates if a search type is supported by this SearchProvider - * Currently only the type SEARCH_GAL (Global Address List) is implemented - * - * @param string $searchtype - * - * @access public - * @return boolean - */ - public function SupportsType($searchtype); - - /** - * Searches the GAL - * - * @param string $searchquery - * @param string $searchrange - * - * @access public - * @return array - * @throws StatusException - */ - public function GetGALSearchResults($searchquery, $searchrange); - - /** - * Searches for the emails on the server - * - * @param ContentParameter $cpo - * - * @return array - */ - public function GetMailboxSearchResults($cpo); - - /** - * Terminates a search for a given PID - * - * @param int $pid - * - * @return boolean - */ - public function TerminateSearch($pid); - - - /** - * Disconnects from the current search provider - * - * @access public - * @return boolean - */ - public function Disconnect(); -} diff --git a/sources/lib/interface/istatemachine.php b/sources/lib/interface/istatemachine.php deleted file mode 100644 index 1fc22a4..0000000 --- a/sources/lib/interface/istatemachine.php +++ /dev/null @@ -1,236 +0,0 @@ -GetStateMachine(). - * Old sync states are not deleted until a new sync state - * is requested. - * At that moment, the PIM is apparently requesting an update - * since sync key X, so any sync states before X are already on - * the PIM, and can therefore be removed. This algorithm should be - * automatically enforced by the IStateMachine implementation. -* -* Created : 02.01.2012 -* -* Copyright 2007 - 2013 Zarafa Deutschland GmbH -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License, version 3, -* as published by the Free Software Foundation with the following additional -* term according to sec. 7: -* -* According to sec. 7 of the GNU Affero General Public License, version 3, -* the terms of the AGPL are supplemented with the following terms: -* -* "Zarafa" is a registered trademark of Zarafa B.V. -* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH -* The licensing of the Program under the AGPL does not imply a trademark license. -* Therefore any rights, title and interest in our trademarks remain entirely with us. -* -* However, if you propagate an unmodified version of the Program you are -* allowed to use the term "Z-Push" to indicate that you distribute the Program. -* Furthermore you may use our trademarks where it is necessary to indicate -* the intended purpose of a product or service provided you use it in accordance -* with honest practices in industrial or commercial matters. -* If you want to propagate modified versions of the Program under the name "Z-Push", -* you may only do so if you have a written permission by Zarafa Deutschland GmbH -* (to acquire a permission please contact Zarafa at trademark@zarafa.com). -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see . -* -* Consult LICENSE file for details -************************************************/ - -interface IStateMachine { - const DEFTYPE = ""; - const DEVICEDATA = "devicedata"; - const FOLDERDATA = "fd"; - const FAILSAVE = "fs"; - const HIERARCHY = "hc"; - const BACKENDSTORAGE = "bs"; - - const STATEVERSION_01 = "1"; // Z-Push 2.0.x - default value if unset - const STATEVERSION_02 = "2"; // Z-Push 2.1.0 Milestone 1 - - /** - * Constructor - * @throws FatalMisconfigurationException - */ - - /** - * Gets a hash value indicating the latest dataset of the named - * state with a specified key and counter. - * If the state is changed between two calls of this method - * the returned hash should be different - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * - * @access public - * @return string - * @throws StateNotFoundException, StateInvalidException - */ - public function GetStateHash($devid, $type, $key = false, $counter = false); - - /** - * Gets a state for a specified key and counter. - * This method sould call IStateMachine->CleanStates() - * to remove older states (same key, previous counters) - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param string $counter (opt) - * @param string $cleanstates (opt) - * - * @access public - * @return mixed - * @throws StateNotFoundException, StateInvalidException - */ - public function GetState($devid, $type, $key = false, $counter = false, $cleanstates = true); - - /** - * Writes ta state to for a key and counter - * - * @param mixed $state - * @param string $devid the device id - * @param string $type the state type - * @param string $key (opt) - * @param int $counter (opt) - * - * @access public - * @return boolean - * @throws StateInvalidException - */ - public function SetState($state, $devid, $type, $key = false, $counter = false); - - /** - * Cleans up all older states - * If called with a $counter, all states previous state counter can be removed - * If called without $counter, all keys (independently from the counter) can be removed - * - * @param string $devid the device id - * @param string $type the state type - * @param string $key - * @param string $counter (opt) - * - * @access public - * @return - * @throws StateInvalidException - */ - public function CleanStates($devid, $type, $key, $counter = false); - - /** - * Links a user to a device - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean indicating if the user was added or not (existed already) - */ - public function LinkUserDevice($username, $devid); - - /** - * Unlinks a device from a user - * - * @param string $username - * @param string $devid - * - * @access public - * @return boolean - */ - public function UnLinkUserDevice($username, $devid); - - /** - * Get all UserDevice mapping - * - * @access public - * @return array - */ - public function GetAllUserDevice(); - - /** - * Returns an array with all device ids for a user. - * If no user is set, all device ids should be returned - * - * @param string $username (opt) - * - * @access public - * @return array - */ - public function GetAllDevices($username = false); - - /** - * Returns the current version of the state files - * - * @access public - * @return int - */ - public function GetStateVersion(); - - /** - * Sets the current version of the state files - * - * @param int $version the new supported version - * - * @access public - * @return boolean - */ - public function SetStateVersion($version); - - /** - * Returns all available states for a device id - * - * @param string $devid the device id - * - * @access public - * @return array(mixed) - */ - public function GetAllStatesForDevice($devid); - - /** - * Retrieves the mapped username for a specific username and backend. - * - * @param string $username The username to lookup - * @param string $backend Name of the backend to lookup - * - * @return string The mapped username or null if none found - */ - public function GetMappedUsername($username, $backend); - - /** - * Maps a username for a specific backend to another username. - * - * @param string $username The username to map - * @param string $backend Name of the backend - * @param string $mappedname The mappend username - * - * @return boolean - */ - public function MapUsername($username, $backend, $mappedname); - - /** - * Unmaps a username for a specific backend. - * - * @param string $username The username to unmap - * @param string $backend Name of the backend - * - * @return boolean - */ - public function UnmapUsername($username, $backend); -} diff --git a/sources/lib/interface/itopcollector.php b/sources/lib/interface/itopcollector.php deleted file mode 100644 index fa5a5d9..0000000 --- a/sources/lib/interface/itopcollector.php +++ /dev/null @@ -1,66 +0,0 @@ -connect(IPC_REDIS_IP, IPC_REDIS_PORT); - self::$redis->select(IPC_REDIS_DATABASE); - } - } - - /** - * Indicates if the shared memory is active - * - * @access public - * @return boolean - */ - public function IsActive() { - $str = "Testing connection"; - return strcmp(self::$redis->echo($str), $str) == 0; - } - - /** - * Reinitializes inter-process data storage - * - * @access public - * @return boolean - */ - public function ReInitSharedMem() { - return self::$redis->flushDB(); - } -} diff --git a/sources/lib/ipc/redis/LoopDetectionRedis.php b/sources/lib/ipc/redis/LoopDetectionRedis.php deleted file mode 100644 index c17411d..0000000 --- a/sources/lib/ipc/redis/LoopDetectionRedis.php +++ /dev/null @@ -1,674 +0,0 @@ -updateProcessStack(); - } - - /** - * Marks the process entry as termineted successfully on the process stack - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionTerminate() { - self::$processentry['end'] = time(); - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionTerminate()"); - $this->updateProcessStack(); - - return true; - } - - /** - * Adds an Exceptions to the process tracking - * - * @param Exception $exception - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionAddException($exception) { - if (!isset(self::$processentry['stat'])) - self::$processentry['stat'] = array(); - self::$processentry['stat'][get_class($exception)] = $exception->getCode(); - $this->updateProcessStack(); - - return true; - } - - /** - * Adds a folderid and connected status code to the process tracking - * - * @param string $folderid - * @param int $status - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionAddStatus($folderid, $status) { - if ($folderid === false) - $folderid = "hierarchy"; - if (!isset(self::$processentry['stat'])) - self::$processentry['stat'] = array(); - self::$processentry['stat'][$folderid] = $status; - $this->updateProcessStack(); - - return true; - } - - /** - * Marks the current process as a PUSH connection - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionSetAsPush() { - self::$processentry['push'] = true; - $this->updateProcessStack(); - - return true; - } - - /** - * Indicates if a full Hierarchy Resync is necessary - * - * In some occasions the mobile tries to sync a folder with an invalid/not-existing ID. - * In these cases a status exception like SYNC_STATUS_FOLDERHIERARCHYCHANGED is returned - * so the mobile executes a FolderSync expecting that some action is taken on that folder (e.g. remove). - * - * If the FolderSync is not doing anything relevant, then the Sync is attempted again - * resulting in the same error and looping between these two processes. - * - * This method checks if in the last process stack a Sync and FolderSync were triggered to - * catch the loop at the 2nd interaction (Sync->FolderSync->Sync->FolderSync => ReSync) - * Ticket: https://jira.zarafa.com/browse/ZP-5 - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionIsHierarchyResyncRequired() { - $seenFailed = array(); - $seenFolderSync = false; - - $lookback = self::$processentry['time'] - 600; // look at the last 5 min - foreach ($this->getProcessStack() as $se) { - if ($se['time'] > $lookback && $se['time'] < (self::$processentry['time']-1)) { - // look for sync command - if (isset($se['stat']) && ($se['cc'] == ZPush::COMMAND_SYNC || $se['cc'] == ZPush::COMMAND_PING)) { - foreach ($se['stat'] as $key => $value) { - if (!isset($seenFailed[$key])) - $seenFailed[$key] = 0; - $seenFailed[$key]++; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen command with Exception or folderid '%s' and code '%s'", $key, $value )); - } - } - // look for FolderSync command with previous failed commands - if ($se['cc'] == ZPush::COMMAND_FOLDERSYNC && !empty($seenFailed) && $se['id'] != self::$processentry['id']) { - // a full folderresync was already triggered - if (isset($se['stat']) && isset($se['stat']['hierarchy']) && $se['stat']['hierarchy'] == SYNC_FSSTATUS_SYNCKEYERROR) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): a full FolderReSync was already requested. Resetting fail counter."); - $seenFailed = array(); - } - else { - $seenFolderSync = true; - if (!empty($seenFailed)) - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen FolderSync after other failing command"); - } - } - } - } - $filtered = array(); - foreach ($seenFailed as $k => $count) { - if ($count>1) - $filtered[] = $k; - } - if ($seenFolderSync && !empty($filtered)) { - ZLog::Write(LOGLEVEL_INFO, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): Potential loop detected. Full hierarchysync indicated."); - - return true; - } - - return false; - } - - /** - * Indicates if a previous process could not be terminated - * - * Checks if there is an end time for the last entry on the stack - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionPreviousConnectionFailed() { - $stack = $this->getProcessStack(); - if (count($stack) > 1) { - $se = $stack[0]; - if (!isset($se['end']) && $se['cc'] != ZPush::COMMAND_PING && !isset($se['push']) ) { - // there is no end time - ZLog::Write(LOGLEVEL_ERROR, sprintf("LoopDetection->ProcessLoopDetectionPreviousConnectionFailed(): Command '%s' at %s with pid '%d' terminated unexpectedly or is still running.", Utils::GetCommandFromCode($se['cc']), Utils::GetFormattedTime($se['time']), $se['pid'])); - ZLog::Write(LOGLEVEL_ERROR, "Please check your logs for this PID and errors like PHP-Fatals or Apache segmentation faults and report your results to the Z-Push dev team."); - } - } - } - - /** - * Gets the PID of an outdated search process - * - * Returns false if there isn't any process - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionGetOutdatedSearchPID() { - $stack = $this->getProcessStack(); - if (count($stack) > 1) { - $se = $stack[0]; - if ($se['cc'] == ZPush::COMMAND_SEARCH) { - return $se['pid']; - } - } - - return false; - } - - /** - * Inserts or updates the current process entry on the stack - * - * @access private - * @return boolean - */ - private function updateProcessStack() { - while (true) { - self::$redis->watch(self::$keystack); - $stack = self::$redis->get(self::$keystack); - if ($stack === false) { - $stack = array(); - } - else { - $stack = unserialize($stack); - } - - // insert/update current process entry - $nstack = array(); - $found = false; - foreach ($stack as $entry) { - if ($entry['id'] != self::$processentry['id']) { - $nstack[] = $entry; - } - else { - $nstack[] = self::$processentry; - $found = true; - } - } - if (!$found) - $nstack[] = self::$processentry; - if (count($nstack) > 10) - $nstack = array_slice($nstack, -10, 10); - // update loop data - if(self::$redis->multi() - ->setex(self::$keystack, self::TTL, serialize($nstack)) - ->exec()) { - return true; - } - else { - ZLog::Write(LOGLEVEL_WARN, "updateProcessStack(): setex just failed (too much concurrency), retrying"); - } - } - } - - /** - * Returns the current process stack - * - * @access private - * @return array - */ - private function getProcessStack() { - $stack = self::$redis->get(self::$keystack); - if ($stack === false) { - return array(); - } - else { - return unserialize($stack); - } - } - - /** - * TRACKING OF BROKEN MESSAGES - * if a previousily ignored message is streamed again to the device it's tracked here - * - * There are two outcomes: - * - next uuid counter is higher than current -> message is fixed and successfully synchronized - * - next uuid counter is the same or uuid changed -> message is still broken - */ - - /** - * Adds a message to the tracking of broken messages - * Being tracked means that a broken message was streamed to the device. - * We save the latest uuid and counter so if on the next sync the counter is higher - * the message was accepted by the device. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return boolean - */ - public function SetBrokenMessage($folderid, $id) { - if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false) - return false; - - $brokenmsg = array('uuid' => $this->broken_message_uuid, 'counter' => $this->broken_message_counter); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->SetBrokenMessage('%s', '%s'): tracking broken message", $folderid, $id)); - self::$redis->multi()->hSet(self::$keybroken . $folderid, $id, serialize($brokenmsg))->expire(self::$keybroken . $folderid, self::TTL)->exec(); - } - - private function RemoveBrokenMessage($folderid, $id) { - $exec = self::$redis->multi()->hDel(self::$keybroken . $folderid, $id)->expire(self::$keybroken . $folderid, self::TTL)->exec(); - - return $exec[0] === 1; - } - - /** - * Gets a list of all ids of a folder which were tracked and which were - * accepted by the device from the last sync. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return array - */ - public function GetSyncedButBeforeIgnoredMessages($folderid) { - if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false) - return array(); - - $removeIds = array(); - $okIds = array(); - $brokenmsgs = self::$redis->hGetAll(self::$keybroken . $folderid); - if (!empty($brokenmsgs)) { - foreach ($brokenmsgs as $id => $data) { - $data = unserialize($data); - // previously broken message was sucessfully synced! - if ($data['uuid'] == $this->broken_message_uuid && $data['counter'] < $this->broken_message_counter) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): message '%s' was successfully synchronized", $folderid, $id)); - $okIds[] = $id; - } - // if the uuid has changed this is old data which should also be removed - if ($data['uuid'] != $this->broken_message_uuid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): stored message id '%s' for uuid '%s' is obsolete", $folderid, $id, $data['uuid'])); - $removeIds[] = $id; - } - } - // remove data - $arg = array_merge(array(self::$keybroken . $folderid), $okIds, $removeIds); - if (count($arg) > 1) - call_user_func_array(array(self::$redis, 'hDel'), $arg); - - //we want to increase the ttl and test if the hash is still there - if (!self::$redis->expire(self::$keybroken . $folderid, self::TTL)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): removed folder from tracking of ignored messages", $folderid)); - } - } - - return $okIds; - } - - /** - * Marks a SyncState as "already used", e.g. when an import process started. - * This is most critical for DiffBackends, as an imported message would be exported again - * in the heartbeat if the notification is triggered before the import is complete. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean - */ - public function SetSyncStateUsage($folderid, $uuid, $counter) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->SetSyncStateUsage(): uuid: %s counter: %d", $uuid, $counter)); - - return self::$redis->hSet(self::$keyfolder . $folderid, 'usage', $counter) !== false; - } - - /** - * Checks if the given counter for a certain uuid+folderid was exported before. - * Returns also true if the counter are the same but previously there were - * changes to be exported. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean indicating if an uuid+counter were exported (with changes) before - */ - public function IsSyncStateObsolete($folderid, $uuid, $counter) { - $current = self::$redis->hGetAll(self::$keyfolder . $folderid); - if (empty($current)) - return false; - $current = unserialize($current); - if (!empty($current)) { - if (!isset($current["uuid"]) || $current["uuid"] != $uuid) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, uuid changed or not set"); - - return true; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IsSyncStateObsolete(): check uuid counter: %d - last known counter: %d with %d queued objects", $counter, $current["count"], $current["queued"])); - - if ($current["uuid"] == $uuid && ($current["count"] > $counter || ($current["count"] == $counter && $current["queued"] > 0) || (isset($current["usage"]) && $current["usage"] >= $counter))) { - $usage = isset($current["usage"]) ? sprintf(" - counter %d already expired",$current["usage"]) : ""; - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, counter already processed". $usage); - - return true; - } - } - } - - return false; - } - - /** - * MESSAGE LOOP DETECTION - */ - - /** - * Loop detection mechanism - * - * 1. request counter is higher than the previous counter (somehow default) - * 1.1) standard situation -> do nothing - * 1.2) loop information exists - * 1.2.1) request counter < maxCounter AND no ignored data -> continue in loop mode - * 1.2.2) request counter < maxCounter AND ignored data -> we have already encountered issue, return to normal - * - * 2. request counter is the same as the previous, but no data was sent on the last request (standard situation) - * - * 3. request counter is the same as the previous and last time objects were sent (loop!) - * 3.1) no loop was detected before, entereing loop mode -> save loop data, loopcount = 1 - * 3.2) loop was detected before, but are gone -> loop resolved - * 3.3) loop was detected before, continuing in loop mode -> this is probably the broken element,loopcount++, - * 3.3.1) item identified, loopcount >= 3 -> ignore item, set ignoredata flag - * - * @param string $folderid the current folder id to be worked on - * @param string $type the type of that folder (Email, Calendar, Contact, Task) - * @param string $uuid the synkkey - * @param string $counter the synckey counter - * @param string $maxItems the current amount of items to be sent to the mobile - * @param string $queuedMessages the amount of messages which were found by the exporter - * - * @access public - * @return boolean when returning true if a loop has been identified - */ - public function Detect($folderid, $type, $uuid, $counter, $maxItems, $queuedMessages) { - $this->broken_message_uuid = $uuid; - $this->broken_message_counter = $counter; - - // if an incoming loop is already detected, do nothing - if ($maxItems === 0 && $queuedMessages > 0) { - ZPush::GetTopCollector()->AnnounceInformation("Incoming loop!", true); - - return true; - } - - $loop = false; - - while (true) { - self::$redis->watch(self::$keyfolder . $folderid); - $current = self::$redis->hGetAll(self::$keyfolder . $folderid); - - // completely new/unknown UUID - if (empty($current)) { - $current = array("type" => $type, "uuid" => $uuid, "count" => $counter-1, "queued" => $queuedMessages); - } - // old UUID in cache - the device requested a new state!! - elseif (isset($current['type']) && $current['type'] == $type && isset($current['uuid']) && $current['uuid'] != $uuid ) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed for folder"); - // some devices (iPhones) may request new UUIDs after broken items were sent several times - if (isset($current['queued']) && $current['queued'] > 0 && (isset($current['maxCount']) && $current['count']+1 < $current['maxCount'] || $counter == 1)) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed and while items where sent to device - forcing loop mode"); - $loop = true; // force loop mode - $current['queued'] = $queuedMessages; - } - else { - $current['queued'] = 0; - } - // set new data, unset old loop information - $current["uuid"] = $uuid; - $current['count'] = $counter; - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - - // see if there are values - if (isset($current['uuid']) && $current['uuid'] == $uuid && isset($current['type']) && $current['type'] == $type && isset($current['count'])) { - - // case 1 - standard, during loop-resolving & resolving - if ($current['count'] < $counter) { - - // case 1.1 - $current['count'] = $counter; - $current['queued'] = $queuedMessages; - if (isset($current["usage"]) && $current["usage"] < $current['count']) - unset($current["usage"]); - // case 1.2 - if (isset($current['maxCount'])) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2 detected"); - // case 1.2.1 - // broken item not identified yet - if (!isset($current['ignored']) && $counter < $current['maxCount']) { - $loop = true; // continue in loop-resolving - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.1 detected"); - } - // case 1.2.2 - if there were any broken items they should be gone, return to normal - else { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.2 detected"); - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - } - } - // case 2 - same counter, but there were no changes before and are there now - elseif ($current['count'] == $counter && $current['queued'] == 0 && $queuedMessages > 0) { - $current['queued'] = $queuedMessages; - if (isset($current["usage"]) && $current["usage"] < $current['count']) - unset($current["usage"]); - } - // case 3 - same counter, changes sent before, hanging loop and ignoring - elseif ($current['count'] == $counter && $current['queued'] > 0) { - if (!isset($current['loopcount'])) { - // case 3.1) we have just encountered a loop! - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.1 detected - loop detected, init loop mode"); - $current['loopcount'] = 1; - // the MaxCount is the max number of messages exported before - $current['maxCount'] = $counter + (($maxItems < $queuedMessages) ? $maxItems : $queuedMessages); - $loop = true; // loop mode!! - } - elseif ($queuedMessages == 0) { - // case 3.2) there was a loop before but now the changes are GONE - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.2 detected - changes gone - clearing loop data"); - $current['queued'] = 0; - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - else { - // case 3.3) still looping the same message! Increase counter - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.3 detected - in loop mode, increase loop counter"); - $current['loopcount']++; - // case 3.3.1 - we got our broken item! - if ($current['loopcount'] >= 3 && isset($current['potential'])) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): case 3.3.1 detected - broken item should be next, attempt to ignore it - id '%s'", $current['potential'])); - $this->ignore_messageid = $current['potential']; - } - $current['maxCount'] = $counter + $queuedMessages; - $loop = true; // loop mode!! - } - } - } - if (isset($current['loopcount'])) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): loop data: loopcount(%d), maxCount(%d), queued(%d), ignored(%s)", $current['loopcount'], $current['maxCount'], $current['queued'], (isset($current['ignored']) ? $current['ignored'] : 'false'))); - - $exec = self::$redis->multi() - ->del(self::$keyfolder . $folderid) - ->hMset(self::$keyfolder . $folderid, $current) - ->expire(self::$keyfolder . $folderid, self::TTL) - ->exec(); - if ($exec[1]) { - break; - } - else { - ZLog::Write(LOGLEVEL_WARN, "Detect($folderid, $type, $uuid, $counter, $maxItems, $queuedMessages): setex just failed (too much concurrency), retrying"); - } - } - - if ($loop == true && $this->ignore_messageid == false) { - ZPush::GetTopCollector()->AnnounceInformation("Loop detection", true); - } - - return $loop; - } - - /** - * Indicates if the next messages should be ignored (not be sent to the mobile!) - * - * @param string $messageid (opt) id of the message which is to be exported next - * @param string $folderid (opt) parent id of the message - * @param boolean $markAsIgnored (opt) to peek without setting the next message to be - * ignored, set this value to false - * @access public - * @return boolean - */ - public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false) { - // as the next message id is not available at all point this method is called, we use different indicators. - // potentialbroken indicates that we know that the broken message should be exported next, - // alltho we do not know for sure as it's export message orders can change - // if the $messageid is available and matches then we are sure and only then really ignore it - - $potentialBroken = false; - $realBroken = false; - if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false) - $potentialBroken = true; - - if ($messageid !== false && $this->ignore_messageid == $messageid) - $realBroken = true; - - // this call is just to know what should be happening - // no further actions necessary - if ($markAsIgnored === false) { - return $potentialBroken; - } - - // we should really do something here - - // first we check if we are in the loop mode, if so, - // we update the potential broken id message so we loop count the same message - - // we found our broken message! - if ($realBroken) { - $this->ignore_messageid = false; - self::$redis->hSet(self::$keyfolder . $folderid, 'ignored', $messageid); - // check if this message was broken before - here we know that it still is and remove it from the tracking - if($this->RemoveBrokenMessage($folderid, $messageid)) - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): previously broken message '$messageid' is still broken and will not be tracked anymore (folder = $folderid)"); - ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true); - } - // not the broken message yet - else { - // update potential id if looping on an item - if (self::$redis->hGet(self::$keyfolder . $folderid, 'loopcount') !== false) { - // this message should be the broken one, but is not!! - // we should reset the loop count because this is certainly not the broken one - if ($potentialBroken) { - self::$redis->hMset(self::$keyfolder . $folderid, array('potential' => $messageid, 'loopcount' => 1)); - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count."); - } - else { - self::$redis->hSet(self::$keyfolder . $folderid, 'potential', $messageid); - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $messageid)); - } - } - - return $realBroken; - } - - /** - * Clears loop detection data - * - * @param string $user (opt) user which data should be removed - * @param string $devid (opt) device id which data to be removed - * - * @return boolean - * @access public - */ - public function ClearData($user = false, $devid = false) { - if ($user == false && $devid == false) - self::$redis->del(self::$redis->keys('ZP-LOOP*')); - elseif ($user == false && $devid != false) - self::$redis->del(self::$redis->keys('ZP-LOOP[^|]*|' . $devid . '|*')); - elseif ($user != false && $devid != false) - self::$redis->del(self::$redis->keys('ZP-LOOP[^|]*|' . $devid . '|' . $user . '*')); - elseif ($user != false && $devid == false) { - self::$redis->del(self::$redis->keys('ZP-LOOP[^|]*|[^|]*|' . $user . '*')); - } - - return true; - } - - /** - * Returns loop detection data for a user and device - * - * @param string $user - * @param string $devid - * - * @return array/boolean returns false if data not available - * @access public - */ - public function GetCachedData($user, $devid) { - //nobody really use it ... - return false; - } -} diff --git a/sources/lib/ipc/redis/PingTrackingRedis.php b/sources/lib/ipc/redis/PingTrackingRedis.php deleted file mode 100644 index 7f62233..0000000 --- a/sources/lib/ipc/redis/PingTrackingRedis.php +++ /dev/null @@ -1,47 +0,0 @@ -key = "ZP-PING|" . self::$devid . '|' . self::$user . '|' . Request::GetAuthDomain(); - } - - /** - * Checks if there are newer ping requests for the same device & user so - * the current process could be terminated - * - * @access public - * @return boolean true if the current process is obsolete - */ - public function DoForcePingTimeout() { - while (true) { - self::$redis->watch($this->key); - $savedtime = self::$redis->get($this->key); - if ($savedtime === false || $savedtime < $_SERVER['REQUEST_TIME']) { - $res = self::$redis->multi()->setex($this->key, self::TTL, $_SERVER['REQUEST_TIME'])->exec(); - if ($res === false) { - ZLog::Write(LOGLEVEL_DEBUG, "DoForcePingTimeout(): set just failed, retrying"); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("DoForcePingTimeout() key: '%s' reqtime: '%s' last_error: '%s'", $this->key, $_SERVER['REQUEST_TIME'], self::$redis->getLastError())); - continue; - } - else { - return false; - } - } - // Don't compare types, only values - if ($savedtime == $_SERVER['REQUEST_TIME']) { - self::$redis->unwatch(); - - return false; - } - if ($savedtime > $_SERVER['REQUEST_TIME']) { - self::$redis->unwatch(); - - return true; - } - } - } -} diff --git a/sources/lib/ipc/redis/REQUIREMENTS b/sources/lib/ipc/redis/REQUIREMENTS deleted file mode 100644 index 9b12a73..0000000 --- a/sources/lib/ipc/redis/REQUIREMENTS +++ /dev/null @@ -1,3 +0,0 @@ -You will need the PHP Redis client in your include_path - -https://github.com/phpredis/phpredis \ No newline at end of file diff --git a/sources/lib/ipc/redis/TopCollectorRedis.php b/sources/lib/ipc/redis/TopCollectorRedis.php deleted file mode 100644 index 3d644cf..0000000 --- a/sources/lib/ipc/redis/TopCollectorRedis.php +++ /dev/null @@ -1,161 +0,0 @@ -preserved = array(); - // static vars come from the parent class - $this->latest = array( "pid" => self::$pid, - "ip" => Request::GetRemoteAddr(), - "user" => self::$user, - "start" => $_SERVER['REQUEST_TIME'], - "devtype" => Request::GetDeviceType(), - "devid" => self::$devid, - "devagent" => Request::GetUserAgent(), - "command" => Request::GetCommandCode(), - "ended" => 0, - "push" => false, - ); - $this->key = self::PREFIX . self::$devid . '|' . self::$user . '|' . self::$pid; - $this->AnnounceInformation("initializing"); - } - - /** - * Destructor - * indicates that the process is shutting down - * - * @access public - */ - public function __destruct() { - $this->AnnounceInformation("OK", false, true); - } - - /** - * Advices all other processes that they should start/stop - * collecting data. The data saved is a timestamp. It has to be - * reactivated every couple of seconds - * - * @param boolean $stop (opt) default false (do collect) - * - * @access public - * @return boolean indicating if it was set to collect before - */ - public function CollectData($stop = false) { - //for now we always collect top data - return true; - } - - /** - * Indicates if the TopCollector is active - * - * @access public - * @return boolean - */ - public function IsActive() { - //for now we always collect top data - return true; - } - - /** - * Announces a string to the TopCollector - * - * @param string $info - * @param boolean $preserve info should be displayed when process terminates - * @param boolean $terminating indicates if the process is terminating - * - * @access public - * @return boolean - */ - public function AnnounceInformation($addinfo, $preserve = false, $terminating = false) { - $this->latest["addinfo"] = $addinfo; - $this->latest["update"] = time(); - - if ($terminating) { - $this->latest["ended"] = time(); - foreach ($this->preserved as $p) - $this->latest["addinfo"] .= " : " . $p; - } - - if ($preserve) - $this->preserved[] = $addinfo; - - $exp = $terminating ? 20 : 120; - self::$redis->setex($this->key,$exp,serialize($this->latest)); - - return true; - } - - /** - * Returns all available top data - * - * @access public - * @return array - */ - public function ReadLatest() { - $topdata = array(); - $keys = self::$redis->keys(self::PREFIX . '*'); - if (!empty($keys)) { - $values = self::$redis->mGet($keys); - $array = array_combine($keys, $values); - foreach ($array as $key => $value) { - if ($value === false) - continue; - $key = explode('|',$key); - if (!array_key_exists($key[1], $topdata)) - $topdata[$key[1]] = array(); - if (!array_key_exists($key[2], $topdata[$key[1]])) - $topdata[$key[1]][$key[2]] = array(); - $topdata[$key[1]][$key[2]][$key[3]] = unserialize($value); - } - } - - return $topdata; - } - - /** - * Cleans up data collected so far - * - * @param boolean $all (optional) if set all data independently from the age is removed - * - * @access public - * @return boolean status - */ - public function ClearLatest($all = false) { - if ($all) - self::$redis->del(self::$redis->keys(self::PREFIX . '*')); - return true; - } - - /** - * Sets a different UserAgent for this connection - * - * @param string $agent - * - * @access public - * @return boolean - */ - public function SetUserAgent($agent) { - $this->latest["devagent"] = $agent; - - return true; - } - - /** - * Marks this process as push connection - * - * @param string $agent - * - * @access public - * @return boolean - */ - public function SetAsPushConnection() { - $this->latest["push"] = true; - - return true; - } -} diff --git a/sources/lib/ipc/shm/interprocessdata.php b/sources/lib/ipc/shm/interprocessdata.php deleted file mode 100644 index 23e0ee2..0000000 --- a/sources/lib/ipc/shm/interprocessdata.php +++ /dev/null @@ -1,275 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -abstract class InterProcessData extends InterProcessStorage { - const CLEANUPTIME = 1; - - protected $type; - protected $allocate; - private $mutexid; - private $memid; - - /** - * Constructor - * - * @access public - */ - public function InterProcessData() { - if (!isset($this->type) || !isset($this->allocate)) - throw new FatalNotImplementedException(sprintf("Class InterProcessData can not be initialized. Subclass %s did not initialize type and allocable memory.", get_class($this))); - - if ($this->InitSharedMem()) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("%s(): Initialized mutexid %s and memid %s.", get_class($this), $this->mutexid, $this->memid)); - } - - /** - * Indicates if the shared memory is active - * - * @access public - * @return boolean - */ - public function IsActive() { - return ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)); - } - - /** - * Reinitializes shared memory by removing, detaching and re-allocating it - * - * @access public - * @return boolean - */ - public function ReInitSharedMem() { - return ($this->RemoveSharedMem() && $this->InitSharedMem()); - } - - /** - * Allocates shared memory - * - * @access private - * @return boolean - */ - private function InitSharedMem() { - // shared mem general "turn off switch" - if (defined("USE_SHARED_MEM") && USE_SHARED_MEM === false) { - ZLog::Write(LOGLEVEL_INFO, "InterProcessData::InitSharedMem(): the usage of shared memory for Z-Push has been disabled. Check your config for 'USE_SHARED_MEM'."); - return false; - } - - if (!function_exists('sem_get') || !function_exists('shm_attach') || !function_exists('sem_acquire')|| !function_exists('shm_get_var')) { - ZLog::Write(LOGLEVEL_INFO, "InterProcessData::InitSharedMem(): PHP libraries for the use shared memory are not available. Functionalities like z-push-top or loop detection are not available. Check your php packages."); - return false; - } - - // Create mutex - $this->mutexid = @sem_get($this->type, 1); - if ($this->mutexid === false) { - ZLog::Write(LOGLEVEL_ERROR, "InterProcessData::InitSharedMem(): could not aquire semaphore"); - return false; - } - - // Attach shared memory - $this->memid = shm_attach($this->type+10, $this->allocate); - if ($this->memid === false) { - ZLog::Write(LOGLEVEL_ERROR, "InterProcessData::InitSharedMem(): could not attach shared memory"); - @sem_remove($this->mutexid); - $this->mutexid = false; - return false; - } - - // TODO mem cleanup has to be implemented - //$this->setInitialCleanTime(); - - return true; - } - - /** - * Removes and detaches shared memory - * - * @access private - * @return boolean - */ - private function RemoveSharedMem() { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) { - @sem_acquire($this->mutexid); - $memid = $this->memid; - $this->memid = false; - @sem_release($this->mutexid); - - @sem_remove($this->mutexid); - @shm_remove($memid); - @shm_detach($memid); - - $this->mutexid = false; - - return true; - } - return false; - } - - /** - * Cleans up the shared memory block - * - * @access public - * @return boolean - */ - public function Clean() { - $stat = false; - - // exclusive block - if ($this->blockMutex()) { - $cleanuptime = ($this->hasData(1)) ? $this->getData(1) : false; - - // TODO implement Shared Memory cleanup - - $this->releaseMutex(); - } - // end exclusive block - - return $stat; - } - - /** - * Blocks the class mutex - * Method blocks until mutex is available! - * ATTENTION: make sure that you *always* release a blocked mutex! - * - * @access protected - * @return boolean - */ - protected function blockMutex() { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) - return @sem_acquire($this->mutexid); - - return false; - } - - /** - * Releases the class mutex - * After the release other processes are able to block the mutex themselfs - * - * @access protected - * @return boolean - */ - protected function releaseMutex() { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) - return @sem_release($this->mutexid); - - return false; - } - - /** - * Indicates if the requested variable is available in shared memory - * - * @param int $id int indicating the variable - * - * @access protected - * @return boolean - */ - protected function hasData($id = 2) { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) { - if (function_exists("shm_has_var")) - return @shm_has_var($this->memid, $id); - else { - $some = $this->getData($id); - return isset($some); - } - } - return false; - } - - /** - * Returns the requested variable from shared memory - * - * @param int $id int indicating the variable - * - * @access protected - * @return mixed - */ - protected function getData($id = 2) { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) - return @shm_get_var($this->memid, $id); - - return ; - } - - /** - * Writes the transmitted variable to shared memory - * Subclasses may never use an id < 2! - * - * @param mixed $data data which should be saved into shared memory - * @param int $id int indicating the variable (bigger than 2!) - * - * @access protected - * @return boolean - */ - protected function setData($data, $id = 2) { - if ((isset($this->mutexid) && $this->mutexid !== false) && (isset($this->memid) && $this->memid !== false)) - return @shm_put_var($this->memid, $id, $data); - - return false; - } - - /** - * Sets the time when the shared memory block was created - * - * @access private - * @return boolean - */ - private function setInitialCleanTime() { - $stat = false; - - // exclusive block - if ($this->blockMutex()) { - - if ($this->hasData(1) == false) - $stat = $this->setData(time(), 1); - - $this->releaseMutex(); - } - // end exclusive block - - return $stat; - } - -} diff --git a/sources/lib/ipc/shm/loopdetection.php b/sources/lib/ipc/shm/loopdetection.php deleted file mode 100644 index 18cc3c5..0000000 --- a/sources/lib/ipc/shm/loopdetection.php +++ /dev/null @@ -1,934 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class LoopDetection extends InterProcessData implements ILoopDetection { - const INTERPROCESSLD = "ipldkey"; - const BROKENMSGS = "bromsgs"; - static private $processident; - static private $processentry; - private $ignore_messageid; - private $broken_message_uuid; - private $broken_message_counter; - - - /** - * Constructor - * - * @access public - */ - public function LoopDetection() { - // initialize super parameters - $this->allocate = 1024000; // 1 MB - $this->type = 1337; - parent::__construct(); - - $this->ignore_messageid = false; - } - - /** - * PROCESS LOOP DETECTION - */ - - /** - * Adds the process entry to the process stack - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionInit() { - return $this->updateProcessStack(); - } - - /** - * Marks the process entry as termineted successfully on the process stack - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionTerminate() { - // just to be sure that the entry is there - self::GetProcessEntry(); - - self::$processentry['end'] = time(); - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionTerminate()"); - return $this->updateProcessStack(); - } - - /** - * Returns a unique identifier for the internal process tracking - * - * @access public - * @return string - */ - private static function GetProcessIdentifier() { - if (!isset(self::$processident)) - self::$processident = sprintf('%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff)); - - return self::$processident; - } - - /** - * Returns a unique entry with informations about the current process - * - * @access public - * @return array - */ - private static function GetProcessEntry() { - if (!isset(self::$processentry)) { - self::$processentry = array(); - self::$processentry['id'] = self::GetProcessIdentifier(); - self::$processentry['pid'] = self::$pid; - self::$processentry['time'] = self::$start; - self::$processentry['cc'] = Request::GetCommandCode(); - } - - return self::$processentry; - } - - /** - * Adds an Exceptions to the process tracking - * - * @param Exception $exception - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionAddException($exception) { - // generate entry if not already there - self::GetProcessEntry(); - - if (!isset(self::$processentry['stat'])) - self::$processentry['stat'] = array(); - - self::$processentry['stat'][get_class($exception)] = $exception->getCode(); - - $this->updateProcessStack(); - return true; - } - - /** - * Adds a folderid and connected status code to the process tracking - * - * @param string $folderid - * @param int $status - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionAddStatus($folderid, $status) { - // generate entry if not already there - self::GetProcessEntry(); - - if ($folderid === false) - $folderid = "hierarchy"; - - if (!isset(self::$processentry['stat'])) - self::$processentry['stat'] = array(); - - self::$processentry['stat'][$folderid] = $status; - - $this->updateProcessStack(); - - return true; - } - - /** - * Marks the current process as a PUSH connection - * - * @access public - * @return boolean - */ - public function ProcessLoopDetectionSetAsPush() { - // generate entry if not already there - self::GetProcessEntry(); - self::$processentry['push'] = true; - - return $this->updateProcessStack(); - } - - /** - * Indicates if a full Hierarchy Resync is necessary - * - * In some occasions the mobile tries to sync a folder with an invalid/not-existing ID. - * In these cases a status exception like SYNC_STATUS_FOLDERHIERARCHYCHANGED is returned - * so the mobile executes a FolderSync expecting that some action is taken on that folder (e.g. remove). - * - * If the FolderSync is not doing anything relevant, then the Sync is attempted again - * resulting in the same error and looping between these two processes. - * - * This method checks if in the last process stack a Sync and FolderSync were triggered to - * catch the loop at the 2nd interaction (Sync->FolderSync->Sync->FolderSync => ReSync) - * Ticket: https://jira.zarafa.com/browse/ZP-5 - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionIsHierarchyResyncRequired() { - $seenFailed = array(); - $seenFolderSync = false; - - $lookback = self::$start - 600; // look at the last 5 min - foreach ($this->getProcessStack() as $se) { - if ($se['time'] > $lookback && $se['time'] < (self::$start-1)) { - // look for sync command - if (isset($se['stat']) && ($se['cc'] == ZPush::COMMAND_SYNC || $se['cc'] == ZPush::COMMAND_PING)) { - foreach($se['stat'] as $key => $value) { - if (!isset($seenFailed[$key])) - $seenFailed[$key] = 0; - $seenFailed[$key]++; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen command with Exception or folderid '%s' and code '%s'", $key, $value )); - } - } - // look for FolderSync command with previous failed commands - if ($se['cc'] == ZPush::COMMAND_FOLDERSYNC && !empty($seenFailed) && $se['id'] != self::GetProcessIdentifier()) { - // a full folderresync was already triggered - if (isset($se['stat']) && isset($se['stat']['hierarchy']) && $se['stat']['hierarchy'] == SYNC_FSSTATUS_SYNCKEYERROR) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): a full FolderReSync was already requested. Resetting fail counter."); - $seenFailed = array(); - } - else { - $seenFolderSync = true; - if (!empty($seenFailed)) - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): seen FolderSync after other failing command"); - } - } - } - } - - $filtered = array(); - foreach ($seenFailed as $k => $count) { - if ($count>1) - $filtered[] = $k; - } - - if ($seenFolderSync && !empty($filtered)) { - ZLog::Write(LOGLEVEL_INFO, "LoopDetection->ProcessLoopDetectionIsHierarchyResyncRequired(): Potential loop detected. Full hierarchysync indicated."); - return true; - } - - return false; - } - - /** - * Indicates if a previous process could not be terminated - * - * Checks if there is an end time for the last entry on the stack - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionPreviousConnectionFailed() { - $stack = $this->getProcessStack(); - if (count($stack) > 1) { - $se = $stack[0]; - if (!isset($se['end']) && $se['cc'] != ZPush::COMMAND_PING && !isset($se['push']) ) { - // there is no end time - ZLog::Write(LOGLEVEL_ERROR, sprintf("LoopDetection->ProcessLoopDetectionPreviousConnectionFailed(): Command '%s' at %s with pid '%d' terminated unexpectedly or is still running.", Utils::GetCommandFromCode($se['cc']), Utils::GetFormattedTime($se['time']), $se['pid'])); - ZLog::Write(LOGLEVEL_ERROR, "Please check your logs for this PID and errors like PHP-Fatals or Apache segmentation faults and report your results to the Z-Push dev team."); - } - } - } - - /** - * Gets the PID of an outdated search process - * - * Returns false if there isn't any process - * - * @access public - * @return boolean - * - */ - public function ProcessLoopDetectionGetOutdatedSearchPID() { - $stack = $this->getProcessStack(); - if (count($stack) > 1) { - $se = $stack[0]; - if ($se['cc'] == ZPush::COMMAND_SEARCH) { - return $se['pid']; - } - } - return false; - } - - /** - * Inserts or updates the current process entry on the stack - * - * @access private - * @return boolean - */ - private function updateProcessStack() { - // initialize params - $this->InitializeParams(); - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, self::INTERPROCESSLD); - - $stack = $loopdata[self::$devid][self::$user][self::INTERPROCESSLD]; - - // insert/update current process entry - $nstack = array(); - $updateentry = self::GetProcessEntry(); - $found = false; - - foreach ($stack as $entry) { - if ($entry['id'] != $updateentry['id']) { - $nstack[] = $entry; - } - else { - $nstack[] = $updateentry; - $found = true; - } - } - - if (!$found) - $nstack[] = $updateentry; - - if (count($nstack) > 10) - $nstack = array_slice($nstack, -10, 10); - - // update loop data - $loopdata[self::$devid][self::$user][self::INTERPROCESSLD] = $nstack; - $ok = $this->setData($loopdata); - - $this->releaseMutex(); - } - // end exclusive block - - return true; - } - - /** - * Returns the current process stack - * - * @access private - * @return array - */ - private function getProcessStack() { - // initialize params - $this->InitializeParams(); - $stack = array(); - - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, self::INTERPROCESSLD); - - $stack = $loopdata[self::$devid][self::$user][self::INTERPROCESSLD]; - - $this->releaseMutex(); - } - // end exclusive block - - return $stack; - } - - /** - * TRACKING OF BROKEN MESSAGES - * if a previousily ignored message is streamed again to the device it's tracked here - * - * There are two outcomes: - * - next uuid counter is higher than current -> message is fixed and successfully synchronized - * - next uuid counter is the same or uuid changed -> message is still broken - */ - - /** - * Adds a message to the tracking of broken messages - * Being tracked means that a broken message was streamed to the device. - * We save the latest uuid and counter so if on the next sync the counter is higher - * the message was accepted by the device. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return boolean - */ - public function SetBrokenMessage($folderid, $id) { - if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false) - return false; - - $ok = false; - $brokenkey = self::BROKENMSGS ."-". $folderid; - - // initialize params - $this->InitializeParams(); - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $brokenkey); - - $brokenmsgs = $loopdata[self::$devid][self::$user][$brokenkey]; - - $brokenmsgs[$id] = array('uuid' => $this->broken_message_uuid, 'counter' => $this->broken_message_counter); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->SetBrokenMessage('%s', '%s'): tracking broken message", $folderid, $id)); - - // update data - $loopdata[self::$devid][self::$user][$brokenkey] = $brokenmsgs; - $ok = $this->setData($loopdata); - - $this->releaseMutex(); - } - // end exclusive block - - return $ok; - } - - /** - * Gets a list of all ids of a folder which were tracked and which were - * accepted by the device from the last sync. - * - * @param string $folderid the parent folder of the message - * @param string $id the id of the message - * - * @access public - * @return array - */ - public function GetSyncedButBeforeIgnoredMessages($folderid) { - if ($folderid == false || !isset($this->broken_message_uuid) || !isset($this->broken_message_counter) || $this->broken_message_uuid == false || $this->broken_message_counter == false) - return array(); - - $brokenkey = self::BROKENMSGS ."-". $folderid; - $removeIds = array(); - $okIds = array(); - - // initialize params - $this->InitializeParams(); - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $brokenkey); - - $brokenmsgs = $loopdata[self::$devid][self::$user][$brokenkey]; - - if (!empty($brokenmsgs)) { - foreach ($brokenmsgs as $id => $data) { - // previously broken message was sucessfully synced! - if ($data['uuid'] == $this->broken_message_uuid && $data['counter'] < $this->broken_message_counter) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): message '%s' was successfully synchronized", $folderid, $id)); - $okIds[] = $id; - } - - // if the uuid has changed this is old data which should also be removed - if ($data['uuid'] != $this->broken_message_uuid) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): stored message id '%s' for uuid '%s' is obsolete", $folderid, $id, $data['uuid'])); - $removeIds[] = $id; - } - } - - // remove data - foreach (array_merge($okIds,$removeIds) as $id) { - unset($brokenmsgs[$id]); - } - - if (empty($brokenmsgs) && isset($loopdata[self::$devid][self::$user][$brokenkey])) { - unset($loopdata[self::$devid][self::$user][$brokenkey]); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->GetSyncedButBeforeIgnoredMessages('%s'): removed folder from tracking of ignored messages", $folderid)); - } - else { - // update data - $loopdata[self::$devid][self::$user][$brokenkey] = $brokenmsgs; - } - $ok = $this->setData($loopdata); - } - - $this->releaseMutex(); - } - // end exclusive block - - return $okIds; - } - - /** - * Marks a SyncState as "already used", e.g. when an import process started. - * This is most critical for DiffBackends, as an imported message would be exported again - * in the heartbeat if the notification is triggered before the import is complete. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean - */ - public function SetSyncStateUsage($folderid, $uuid, $counter) { - // initialize params - $this->InitializeParams(); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->SetSyncStateUsage(): uuid: %s counter: %d", $uuid, $counter)); - - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $folderid); - $current = $loopdata[self::$devid][self::$user][$folderid]; - - - // update the usage flag - $current["usage"] = $counter; - - // update loop data - $loopdata[self::$devid][self::$user][$folderid] = $current; - $ok = $this->setData($loopdata); - - $this->releaseMutex(); - } - // end exclusive block - } - - /** - * Checks if the given counter for a certain uuid+folderid was exported before. - * Returns also true if the counter are the same but previously there were - * changes to be exported. - * - * @param string $folderid folder id - * @param string $uuid synkkey - * @param string $counter synckey counter - * - * @access public - * @return boolean indicating if an uuid+counter were exported (with changes) before - */ - public function IsSyncStateObsolete($folderid, $uuid, $counter) { - // initialize params - $this->InitializeParams(); - - $obsolete = false; - - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - $this->releaseMutex(); - // end exclusive block - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $folderid); - - $current = $loopdata[self::$devid][self::$user][$folderid]; - - if (!empty($current)) { - if (!isset($current["uuid"]) || $current["uuid"] != $uuid) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, uuid changed or not set"); - $obsolete = true; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IsSyncStateObsolete(): check uuid counter: %d - last known counter: %d with %d queued objects", $counter, $current["count"], $current["queued"])); - - if ($current["uuid"] == $uuid && ($current["count"] > $counter || ($current["count"] == $counter && $current["queued"] > 0) || (isset($current["usage"]) && $current["usage"] >= $counter))) { - $usage = isset($current["usage"]) ? sprintf(" - counter %d already expired",$current["usage"]) : ""; - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IsSyncStateObsolete(): yes, counter already processed". $usage); - $obsolete = true; - } - } - } - } - - return $obsolete; - } - - /** - * MESSAGE LOOP DETECTION - */ - - /** - * Loop detection mechanism - * - * 1. request counter is higher than the previous counter (somehow default) - * 1.1) standard situation -> do nothing - * 1.2) loop information exists - * 1.2.1) request counter < maxCounter AND no ignored data -> continue in loop mode - * 1.2.2) request counter < maxCounter AND ignored data -> we have already encountered issue, return to normal - * - * 2. request counter is the same as the previous, but no data was sent on the last request (standard situation) - * - * 3. request counter is the same as the previous and last time objects were sent (loop!) - * 3.1) no loop was detected before, entereing loop mode -> save loop data, loopcount = 1 - * 3.2) loop was detected before, but are gone -> loop resolved - * 3.3) loop was detected before, continuing in loop mode -> this is probably the broken element,loopcount++, - * 3.3.1) item identified, loopcount >= 3 -> ignore item, set ignoredata flag - * - * @param string $folderid the current folder id to be worked on - * @param string $type the type of that folder (Email, Calendar, Contact, Task) - * @param string $uuid the synkkey - * @param string $counter the synckey counter - * @param string $maxItems the current amount of items to be sent to the mobile - * @param string $queuedMessages the amount of messages which were found by the exporter - * - * @access public - * @return boolean when returning true if a loop has been identified - */ - public function Detect($folderid, $type, $uuid, $counter, $maxItems, $queuedMessages) { - $this->broken_message_uuid = $uuid; - $this->broken_message_counter = $counter; - - // if an incoming loop is already detected, do nothing - if ($maxItems === 0 && $queuedMessages > 0) { - ZPush::GetTopCollector()->AnnounceInformation("Incoming loop!", true); - return true; - } - - // initialize params - $this->InitializeParams(); - - $loop = false; - - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $folderid); - - $current = $loopdata[self::$devid][self::$user][$folderid]; - - // completely new/unknown UUID - if (empty($current)) - $current = array("type" => $type, "uuid" => $uuid, "count" => $counter-1, "queued" => $queuedMessages); - - // old UUID in cache - the device requested a new state!! - else if (isset($current['type']) && $current['type'] == $type && isset($current['uuid']) && $current['uuid'] != $uuid ) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed for folder"); - - // some devices (iPhones) may request new UUIDs after broken items were sent several times - if (isset($current['queued']) && $current['queued'] > 0 && - (isset($current['maxCount']) && $current['count']+1 < $current['maxCount'] || $counter == 1)) { - - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): UUID changed and while items where sent to device - forcing loop mode"); - $loop = true; // force loop mode - $current['queued'] = $queuedMessages; - } - else { - $current['queued'] = 0; - } - - // set new data, unset old loop information - $current["uuid"] = $uuid; - $current['count'] = $counter; - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - - // see if there are values - if (isset($current['uuid']) && $current['uuid'] == $uuid && - isset($current['type']) && $current['type'] == $type && - isset($current['count'])) { - - // case 1 - standard, during loop-resolving & resolving - if ($current['count'] < $counter) { - - // case 1.1 - $current['count'] = $counter; - $current['queued'] = $queuedMessages; - if (isset($current["usage"]) && $current["usage"] < $current['count']) - unset($current["usage"]); - - // case 1.2 - if (isset($current['maxCount'])) { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2 detected"); - - // case 1.2.1 - // broken item not identified yet - if (!isset($current['ignored']) && $counter < $current['maxCount']) { - $loop = true; // continue in loop-resolving - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.1 detected"); - } - // case 1.2.2 - if there were any broken items they should be gone, return to normal - else { - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 1.2.2 detected"); - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - } - } - - // case 2 - same counter, but there were no changes before and are there now - else if ($current['count'] == $counter && $current['queued'] == 0 && $queuedMessages > 0) { - $current['queued'] = $queuedMessages; - if (isset($current["usage"]) && $current["usage"] < $current['count']) - unset($current["usage"]); - } - - // case 3 - same counter, changes sent before, hanging loop and ignoring - else if ($current['count'] == $counter && $current['queued'] > 0) { - - if (!isset($current['loopcount'])) { - // case 3.1) we have just encountered a loop! - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.1 detected - loop detected, init loop mode"); - $current['loopcount'] = 1; - // the MaxCount is the max number of messages exported before - $current['maxCount'] = $counter + (($maxItems < $queuedMessages)? $maxItems: $queuedMessages); - $loop = true; // loop mode!! - } - else if ($queuedMessages == 0) { - // case 3.2) there was a loop before but now the changes are GONE - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.2 detected - changes gone - clearing loop data"); - $current['queued'] = 0; - unset($current['loopcount']); - unset($current['ignored']); - unset($current['maxCount']); - unset($current['potential']); - } - else { - // case 3.3) still looping the same message! Increase counter - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->Detect(): case 3.3 detected - in loop mode, increase loop counter"); - $current['loopcount']++; - - // case 3.3.1 - we got our broken item! - if ($current['loopcount'] >= 3 && isset($current['potential'])) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): case 3.3.1 detected - broken item should be next, attempt to ignore it - id '%s'", $current['potential'])); - $this->ignore_messageid = $current['potential']; - } - $current['maxCount'] = $counter + $queuedMessages; - $loop = true; // loop mode!! - } - } - - } - if (isset($current['loopcount'])) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->Detect(): loop data: loopcount(%d), maxCount(%d), queued(%d), ignored(%s)", $current['loopcount'], $current['maxCount'], $current['queued'], (isset($current['ignored'])?$current['ignored']:'false'))); - - // update loop data - $loopdata[self::$devid][self::$user][$folderid] = $current; - $ok = $this->setData($loopdata); - - $this->releaseMutex(); - } - // end exclusive block - - if ($loop == true && $this->ignore_messageid == false) { - ZPush::GetTopCollector()->AnnounceInformation("Loop detection", true); - } - - return $loop; - } - - /** - * Indicates if the next messages should be ignored (not be sent to the mobile!) - * - * @param string $messageid (opt) id of the message which is to be exported next - * @param string $folderid (opt) parent id of the message - * @param boolean $markAsIgnored (opt) to peek without setting the next message to be - * ignored, set this value to false - * @access public - * @return boolean - */ - public function IgnoreNextMessage($markAsIgnored = true, $messageid = false, $folderid = false) { - // as the next message id is not available at all point this method is called, we use different indicators. - // potentialbroken indicates that we know that the broken message should be exported next, - // alltho we do not know for sure as it's export message orders can change - // if the $messageid is available and matches then we are sure and only then really ignore it - - $potentialBroken = false; - $realBroken = false; - if (Request::GetCommandCode() == ZPush::COMMAND_SYNC && $this->ignore_messageid !== false) - $potentialBroken = true; - - if ($messageid !== false && $this->ignore_messageid == $messageid) - $realBroken = true; - - // this call is just to know what should be happening - // no further actions necessary - if ($markAsIgnored === false) { - return $potentialBroken; - } - - // we should really do something here - - // first we check if we are in the loop mode, if so, - // we update the potential broken id message so we loop count the same message - - $changedData = false; - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - // check and initialize the array structure - $this->checkArrayStructure($loopdata, $folderid); - - $current = $loopdata[self::$devid][self::$user][$folderid]; - - // we found our broken message! - if ($realBroken) { - $this->ignore_messageid = false; - $current['ignored'] = $messageid; - $changedData = true; - - // check if this message was broken before - here we know that it still is and remove it from the tracking - $brokenkey = self::BROKENMSGS ."-". $folderid; - if (isset($loopdata[self::$devid][self::$user][$brokenkey]) && isset($loopdata[self::$devid][self::$user][$brokenkey][$messageid])) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): previously broken message '%s' is still broken and will not be tracked anymore", $messageid)); - unset($loopdata[self::$devid][self::$user][$brokenkey][$messageid]); - } - } - // not the broken message yet - else { - // update potential id if looping on an item - if (isset($current['loopcount'])) { - $current['potential'] = $messageid; - - // this message should be the broken one, but is not!! - // we should reset the loop count because this is certainly not the broken one - if ($potentialBroken) { - $current['loopcount'] = 1; - ZLog::Write(LOGLEVEL_DEBUG, "LoopDetection->IgnoreNextMessage(): this should be the broken one, but is not! Resetting loop count."); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("LoopDetection->IgnoreNextMessage(): Loop mode, potential broken message id '%s'", $current['potential'])); - - $changedData = true; - } - } - - // update loop data - if ($changedData == true) { - $loopdata[self::$devid][self::$user][$folderid] = $current; - $ok = $this->setData($loopdata); - } - - $this->releaseMutex(); - } - // end exclusive block - - if ($realBroken) - ZPush::GetTopCollector()->AnnounceInformation("Broken message ignored", true); - - return $realBroken; - } - - /** - * Clears loop detection data - * - * @param string $user (opt) user which data should be removed - user can not be specified without - * @param string $devid (opt) device id which data to be removed - * - * @return boolean - * @access public - */ - public function ClearData($user = false, $devid = false) { - $stat = true; - $ok = false; - - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - - if ($user == false && $devid == false) - $loopdata = array(); - elseif ($user == false && $devid != false) - $loopdata[$devid] = array(); - elseif ($user != false && $devid != false) - $loopdata[$devid][$user] = array(); - elseif ($user != false && $devid == false) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Not possible to reset loop detection data for user '%s' without a specifying a device id", $user)); - $stat = false; - } - - if ($stat) - $ok = $this->setData($loopdata); - - $this->releaseMutex(); - } - // end exclusive block - - return $stat && $ok; - } - - /** - * Returns loop detection data for a user and device - * - * @param string $user - * @param string $devid - * - * @return array/boolean returns false if data not available - * @access public - */ - public function GetCachedData($user, $devid) { - // exclusive block - if ($this->blockMutex()) { - $loopdata = ($this->hasData()) ? $this->getData() : array(); - $this->releaseMutex(); - } - // end exclusive block - if (isset($loopdata) && isset($loopdata[$devid]) && isset($loopdata[$devid][$user])) - return $loopdata[$devid][$user]; - - return false; - } - - /** - * Builds an array structure for the loop detection data - * - * @param array $loopdata reference to the topdata array - * - * @access private - * @return - */ - private function checkArrayStructure(&$loopdata, $folderid) { - if (!isset($loopdata) || !is_array($loopdata)) - $loopdata = array(); - - if (!isset($loopdata[self::$devid])) - $loopdata[self::$devid] = array(); - - if (!isset($loopdata[self::$devid][self::$user])) - $loopdata[self::$devid][self::$user] = array(); - - if (!isset($loopdata[self::$devid][self::$user][$folderid])) - $loopdata[self::$devid][self::$user][$folderid] = array(); - } -} diff --git a/sources/lib/ipc/shm/pingtracking.php b/sources/lib/ipc/shm/pingtracking.php deleted file mode 100644 index 5cd8c05..0000000 --- a/sources/lib/ipc/shm/pingtracking.php +++ /dev/null @@ -1,154 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class PingTracking extends InterProcessData implements IPingTracking { - - /** - * Constructor - * - * @access public - */ - public function PingTracking() { - // initialize super parameters - $this->allocate = 512000; // 500 KB - $this->type = 2; - parent::__construct(); - - $this->initPing(); - } - - /** - * Destructor - * Used to remove the current ping data from shared memory - * - * @access public - */ - public function __destruct() { - // exclusive block - if ($this->blockMutex()) { - $pings = $this->getData(); - - // check if our ping is still in the list - if (isset($pings[self::$devid][self::$user][self::$pid])) { - unset($pings[self::$devid][self::$user][self::$pid]); - $stat = $this->setData($pings); - } - - $this->releaseMutex(); - } - // end exclusive block - } - - /** - * Initialized the current request - * - * @access public - * @return boolean - */ - protected function initPing() { - $stat = false; - - // initialize params - $this->InitializeParams(); - - // exclusive block - if ($this->blockMutex()) { - $pings = ($this->hasData()) ? $this->getData() : array(); - - // set the start time for the current process - $this->checkArrayStructure($pings); - $pings[self::$devid][self::$user][self::$pid] = self::$start; - $stat = $this->setData($pings); - $this->releaseMutex(); - } - // end exclusive block - - return $stat; - } - - /** - * Checks if there are newer ping requests for the same device & user so - * the current process could be terminated - * - * @access public - * @return boolean true if the current process is obsolete - */ - public function DoForcePingTimeout() { - $pings = false; - // exclusive block - if ($this->blockMutex()) { - $pings = $this->getData(); - $this->releaseMutex(); - } - // end exclusive block - - // check if there is another (and newer) active ping connection - if (is_array($pings) && isset($pings[self::$devid][self::$user]) && count($pings[self::$devid][self::$user]) > 1) { - foreach ($pings[self::$devid][self::$user] as $pid=>$starttime) - if ($starttime > self::$start) - return true; - } - - return false; - } - - /** - * Builds an array structure for the concurrent ping connection detection - * - * @param array $array reference to the ping data array - * - * @access private - * @return - */ - private function checkArrayStructure(&$array) { - if (!is_array($array)) - $array = array(); - - if (!isset($array[self::$devid])) - $array[self::$devid] = array(); - - if (!isset($array[self::$devid][self::$user])) - $array[self::$devid][self::$user] = array(); - - } -} diff --git a/sources/lib/ipc/shm/topcollector.php b/sources/lib/ipc/shm/topcollector.php deleted file mode 100644 index ec9416c..0000000 --- a/sources/lib/ipc/shm/topcollector.php +++ /dev/null @@ -1,298 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class TopCollector extends InterProcessData implements ITopCollector { - const ENABLEDAT = 2; - const TOPDATA = 3; - - protected $preserved; - protected $latest; - - /** - * Constructor - * - * @access public - */ - public function TopCollector() { - // initialize super parameters - $this->allocate = 2097152; // 2 MB - $this->type = 20; - parent::__construct(); - - // initialize params - $this->InitializeParams(); - - $this->preserved = array(); - // static vars come from the parent class - $this->latest = array( "pid" => self::$pid, - "ip" => Request::GetRemoteAddr(), - "user" => self::$user, - "start" => self::$start, - "devtype" => Request::GetDeviceType(), - "devid" => self::$devid, - "devagent" => Request::GetUserAgent(), - "command" => Request::GetCommandCode(), - "ended" => 0, - "push" => false, - ); - - $this->AnnounceInformation("initializing"); - } - - /** - * Destructor - * indicates that the process is shutting down - * - * @access public - */ - public function __destruct() { - $this->AnnounceInformation("OK", false, true); - } - - /** - * Advices all other processes that they should start/stop - * collecting data. The data saved is a timestamp. It has to be - * reactivated every couple of seconds - * - * @param boolean $stop (opt) default false (do collect) - * - * @access public - * @return boolean indicating if it was set to collect before - */ - public function CollectData($stop = false) { - $wasEnabled = false; - - // exclusive block - if ($this->blockMutex()) { - $wasEnabled = ($this->hasData(self::ENABLEDAT)) ? $this->getData(self::ENABLEDAT) : false; - - $time = time(); - if ($stop === true) $time = 0; - - if (! $this->setData($time, self::ENABLEDAT)) - return false; - $this->releaseMutex(); - } - // end exclusive block - - return $wasEnabled; - } - - /** - * Announces a string to the TopCollector - * - * @param string $info - * @param boolean $preserve info should be displayed when process terminates - * @param boolean $terminating indicates if the process is terminating - * - * @access public - * @return boolean - */ - public function AnnounceInformation($addinfo, $preserve = false, $terminating = false) { - $this->latest["addinfo"] = $addinfo; - $this->latest["update"] = time(); - - if ($terminating) { - $this->latest["ended"] = time(); - foreach ($this->preserved as $p) - $this->latest["addinfo"] .= " : ".$p; - } - - if ($preserve) - $this->preserved[] = $addinfo; - - // exclusive block - $ok = true; - if ($this->blockMutex()) { - - if ($this->isEnabled()) { - $topdata = ($this->hasData(self::TOPDATA)) ? $this->getData(self::TOPDATA): array(); - - $this->checkArrayStructure($topdata); - - // update - $topdata[self::$devid][self::$user][self::$pid] = $this->latest; - $ok = $this->setData($topdata, self::TOPDATA); - } - $this->releaseMutex(); - } - // end exclusive block - - if (!$ok) { - ZLog::Write(LOGLEVEL_WARN, "TopCollector::AnnounceInformation(): could not write to shared memory. Z-Push top will not display this data."); - return false; - } - - return true; - } - - /** - * Returns all available top data - * - * @access public - * @return array - */ - public function ReadLatest() { - $topdata = array(); - - // exclusive block - if ($this->blockMutex()) { - $topdata = ($this->hasData(self::TOPDATA)) ? $this->getData(self::TOPDATA) : array(); - $this->releaseMutex(); - } - // end exclusive block - - return $topdata; - } - - /** - * Cleans up data collected so far - * - * @param boolean $all (optional) if set all data independently from the age is removed - * - * @access public - * @return boolean status - */ - public function ClearLatest($all = false) { - // it's ok when doing this every 10 sec - if ($all == false && time() % 10 != 0 ) - return true; - - $stat = false; - - // exclusive block - if ($this->blockMutex()) { - if ($all == true) { - $topdata = array(); - } - else { - $topdata = ($this->hasData(self::TOPDATA)) ? $this->getData(self::TOPDATA) : array(); - - $toClear = array(); - foreach ($topdata as $devid=>$users) { - foreach ($users as $user=>$pids) { - foreach ($pids as $pid=>$line) { - // remove everything which terminated for 20 secs or is not updated for more than 120 secs - if (($line["ended"] != 0 && time() - $line["ended"] > 20) || - time() - $line["update"] > 120) { - $toClear[] = array($devid, $user, $pid); - } - } - } - } - foreach ($toClear as $tc) - unset($topdata[$tc[0]][$tc[1]][$tc[2]]); - } - - $stat = $this->setData($topdata, self::TOPDATA); - $this->releaseMutex(); - } - // end exclusive block - - return $stat; - } - - /** - * Sets a different UserAgent for this connection - * - * @param string $agent - * - * @access public - * @return boolean - */ - public function SetUserAgent($agent) { - $this->latest["devagent"] = $agent; - return true; - } - - /** - * Marks this process as push connection - * - * @param string $agent - * - * @access public - * @return boolean - */ - public function SetAsPushConnection() { - $this->latest["push"] = true; - return true; - } - - /** - * Indicates if top data should be saved or not - * Returns true for 10 seconds after the latest CollectData() - * SHOULD only be called with locked mutex! - * - * @access private - * @return boolean - */ - private function isEnabled() { - $isEnabled = ($this->hasData(self::ENABLEDAT)) ? $this->getData(self::ENABLEDAT) : false; - return ($isEnabled !== false && ($isEnabled +300) > time()); - } - - /** - * Builds an array structure for the top data - * - * @param array $topdata reference to the topdata array - * - * @access private - * @return - */ - private function checkArrayStructure(&$topdata) { - if (!isset($topdata) || !is_array($topdata)) - $topdata = array(); - - if (!isset($topdata[self::$devid])) - $topdata[self::$devid] = array(); - - if (!isset($topdata[self::$devid][self::$user])) - $topdata[self::$devid][self::$user] = array(); - - if (!isset($topdata[self::$devid][self::$user][self::$pid])) - $topdata[self::$devid][self::$user][self::$pid] = array(); - } -} diff --git a/sources/lib/request/folderchange.php b/sources/lib/request/folderchange.php deleted file mode 100644 index 47be4c9..0000000 --- a/sources/lib/request/folderchange.php +++ /dev/null @@ -1,246 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FolderChange extends RequestProcessor { - - /** - * Handles creates, updates or deletes of a folder - * issued by the commands FolderCreate, FolderUpdate and FolderDelete - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle ($commandCode) { - $el = self::$decoder->getElement(); - - if($el[EN_TYPE] != EN_TYPE_STARTTAG) - return false; - - $create = $update = $delete = false; - if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERCREATE) - $create = true; - else if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERUPDATE) - $update = true; - else if($el[EN_TAG] == SYNC_FOLDERHIERARCHY_FOLDERDELETE) - $delete = true; - - if(!$create && !$update && !$delete) - return false; - - // SyncKey - if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) - return false; - $synckey = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - - // ServerID - $serverid = false; - if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID)) { - $serverid = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - // Parent - $parentid = false; - - // when creating or updating more information is necessary - if (!$delete) { - if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_PARENTID)) { - $parentid = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - // Displayname - if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_DISPLAYNAME)) - return false; - $displayname = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - - // Type - $type = false; - if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_TYPE)) { - $type = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - } - - // endtag foldercreate, folderupdate, folderdelete - if(!self::$decoder->getElementEndTag()) - return false; - - $status = SYNC_FSSTATUS_SUCCESS; - // Get state of hierarchy - try { - $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey); - $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey); - - // Over the ChangesWrapper the HierarchyCache is notified about all changes - $changesMem = self::$deviceManager->GetHierarchyChangesWrapper(); - - // the hierarchyCache should now fully be initialized - check for changes in the additional folders - $changesMem->Config(ZPush::GetAdditionalSyncFolders()); - - // there are unprocessed changes in the hierarchy, trigger resync - if ($changesMem->GetChangeCount() > 0) - throw new StatusException("HandleFolderChange() can not proceed as there are unprocessed hierarchy changes", SYNC_FSSTATUS_SERVERERROR); - - // any additional folders can not be modified! - if ($serverid !== false && ZPush::GetAdditionalSyncFolderStore($serverid)) - throw new StatusException("HandleFolderChange() can not change additional folders which are configured", SYNC_FSSTATUS_SYSTEMFOLDER); - - // switch user store if this this happens inside an additional folder - // if this is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore((($parentid != false)?$parentid:$serverid)))) - throw new StatusException(sprintf("HandleFolderChange() could not Setup() the backend for folder id '%s'", (($parentid != false)?$parentid:$serverid)), SYNC_FSSTATUS_SERVERERROR); - } - catch (StateNotFoundException $snfex) { - $status = SYNC_FSSTATUS_SYNCKEYERROR; - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - // set $newsynckey in case of an error - if (!isset($newsynckey)) - $newsynckey = $synckey; - - if ($status == SYNC_FSSTATUS_SUCCESS) { - try { - // Configure importer with last state - $importer = self::$backend->GetImporter(); - $importer->Config($syncstate); - - // the messages from the PIM will be forwarded to the real importer - $changesMem->SetDestinationImporter($importer); - - // process incoming change - if (!$delete) { - // Send change - $folder = new SyncFolder(); - $folder->serverid = $serverid; - $folder->parentid = $parentid; - $folder->displayname = $displayname; - $folder->type = $type; - - $serverid = $changesMem->ImportFolderChange($folder); - } - else { - // delete folder - $changesMem->ImportFolderDeletion($serverid, 0); - } - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - } - - self::$encoder->startWBXML(); - if ($create) { - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERCREATE); - { - { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); - self::$encoder->content($newsynckey); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SERVERENTRYID); - self::$encoder->content($serverid); - self::$encoder->endTag(); - } - } - self::$encoder->endTag(); - } - - elseif ($update) { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERUPDATE); - { - { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); - self::$encoder->content($newsynckey); - self::$encoder->endTag(); - } - } - self::$encoder->endTag(); - } - - elseif ($delete) { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERDELETE); - { - { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); - self::$encoder->content($newsynckey); - self::$encoder->endTag(); - } - } - self::$encoder->endTag(); - } - - self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true); - - // Save the sync state for the next time - if (isset($importer)) - self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $importer->GetState()); - - return true; - } -} diff --git a/sources/lib/request/foldersync.php b/sources/lib/request/foldersync.php deleted file mode 100644 index 18c6db0..0000000 --- a/sources/lib/request/foldersync.php +++ /dev/null @@ -1,260 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class FolderSync extends RequestProcessor { - - /** - * Handles the FolderSync command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle ($commandCode) { - // Parse input - if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_SYNCKEY)) - return false; - - $synckey = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - - // every FolderSync with SyncKey 0 should return the supported AS version & command headers - if($synckey == "0") { - self::$specialHeaders = array(); - self::$specialHeaders[] = ZPush::GetSupportedProtocolVersions(); - self::$specialHeaders[] = ZPush::GetSupportedCommands(); - } - - $status = SYNC_FSSTATUS_SUCCESS; - $newsynckey = $synckey; - try { - $syncstate = self::$deviceManager->GetStateManager()->GetSyncState($synckey); - - // We will be saving the sync state under 'newsynckey' - $newsynckey = self::$deviceManager->GetStateManager()->GetNewSyncKey($synckey); - } - catch (StateNotFoundException $snfex) { - $status = SYNC_FSSTATUS_SYNCKEYERROR; - } - catch (StateInvalidException $sive) { - $status = SYNC_FSSTATUS_SYNCKEYERROR; - } - - // The ChangesWrapper caches all imports in-memory, so we can send a change count - // before sending the actual data. - // the HierarchyCache is notified and the changes from the PIM are transmitted to the actual backend - $changesMem = self::$deviceManager->GetHierarchyChangesWrapper(); - - // the hierarchyCache should now fully be initialized - check for changes in the additional folders - $changesMem->Config(ZPush::GetAdditionalSyncFolders()); - - // process incoming changes - if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_CHANGES)) { - // Ignore if present - if(self::$decoder->getElementStartTag(SYNC_FOLDERHIERARCHY_COUNT)) { - self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - // Process the changes (either , , or ) - $element = self::$decoder->getElement(); - - if($element[EN_TYPE] != EN_TYPE_STARTTAG) - return false; - - $importer = false; - while(1) { - $folder = new SyncFolder(); - if(!$folder->Decode(self::$decoder)) - break; - - try { - if ($status == SYNC_FSSTATUS_SUCCESS && !$importer) { - // Configure the backends importer with last state - $importer = self::$backend->GetImporter(); - $importer->Config($syncstate); - // the messages from the PIM will be forwarded to the backend - $changesMem->forwardImporter($importer); - } - - if ($status == SYNC_FSSTATUS_SUCCESS) { - switch($element[EN_TAG]) { - case SYNC_ADD: - case SYNC_MODIFY: - $serverid = $changesMem->ImportFolderChange($folder); - break; - case SYNC_REMOVE: - $serverid = $changesMem->ImportFolderDeletion($folder); - break; - } - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): ignoring incoming folderchange for folder '%s' as status indicates problem.", $folder->displayname)); - self::$topCollector->AnnounceInformation("Incoming change ignored", true); - } - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - } - - if(!self::$decoder->getElementEndTag()) - return false; - } - // no incoming changes - else { - // check for a potential process loop like described in Issue ZP-5 - if ($synckey != "0" && self::$deviceManager->IsHierarchyFullResyncRequired()) - $status = SYNC_FSSTATUS_SYNCKEYERROR; - self::$deviceManager->AnnounceProcessStatus(false, $status); - } - - if(!self::$decoder->getElementEndTag()) - return false; - - // We have processed incoming foldersync requests, now send the PIM - // our changes - - // Output our WBXML reply now - self::$encoder->StartWBXML(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERSYNC); - { - if ($status == SYNC_FSSTATUS_SUCCESS) { - try { - // do nothing if this is an invalid device id (like the 'validate' Androids internal client sends) - if (!Request::IsValidDeviceID()) - throw new StatusException(sprintf("Request::IsValidDeviceID() indicated that '%s' is not a valid device id", Request::GetDeviceID()), SYNC_FSSTATUS_SERVERERROR); - - // Changes from backend are sent to the MemImporter and processed for the HierarchyCache. - // The state which is saved is from the backend, as the MemImporter is only a proxy. - $exporter = self::$backend->GetExporter(); - - $exporter->Config($syncstate); - $exporter->InitializeExporter($changesMem); - - // Stream all changes to the ImportExportChangesMem - $maxExporttime = Request::GetExpectedConnectionTimeout(); - $totalChanges = $exporter->GetChangeCount(); - $started = time(); - $exported = 0; - $partial = false; - while(is_array($exporter->Synchronize())) { - $exported++; - - if (time() % 4 ) { - self::$topCollector->AnnounceInformation(sprintf("Exported %d from %d folders", $exported, $totalChanges)); - } - - // if partial sync is allowed, stop if this takes too long - if (USE_PARTIAL_FOLDERSYNC && (time() - $started) > $maxExporttime) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Request->HandleFolderSync(): Exporting folders is too slow. In %d seconds only %d from %d changes were processed.",(time() - $started), $exported, $totalChanges)); - self::$topCollector->AnnounceInformation(sprintf("Partial export of %d out of %d folders", $exported, $totalChanges), true); - self::$deviceManager->SetFolderSyncComplete(false); - $partial = true; - break; - } - } - - // update the foldersync complete flag - if (USE_PARTIAL_FOLDERSYNC && $partial == false && self::$deviceManager->GetFolderSyncComplete() === false) { - // say that we are done with partial synching - self::$deviceManager->SetFolderSyncComplete(true); - // reset the loop data to prevent any loop detection to kick in now - self::$deviceManager->ClearLoopDetectionData(Request::GetAuthUser(), Request::GetDeviceID()); - ZLog::Write(LOGLEVEL_INFO, "Request->HandleFolderSync(): Chunked exporting of folders completed successfully"); - } - - // get the new state from the backend - $newsyncstate = (isset($exporter))?$exporter->GetState():""; - } - catch (StatusException $stex) { - if ($stex->getCode() == SYNC_FSSTATUS_CODEUNKNOWN) - $status = SYNC_FSSTATUS_SYNCKEYERROR; - else - $status = $stex->getCode(); - } - } - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - if ($status == SYNC_FSSTATUS_SUCCESS) { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_SYNCKEY); - $synckey = ($changesMem->IsStateChanged()) ? $newsynckey : $synckey; - self::$encoder->content($synckey); - self::$encoder->endTag(); - - // Stream folders directly to the PDA - $streamimporter = new ImportChangesStream(self::$encoder, false); - $changesMem->InitializeExporter($streamimporter); - $changeCount = $changesMem->GetChangeCount(); - - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_CHANGES); - { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_COUNT); - self::$encoder->content($changeCount); - self::$encoder->endTag(); - while($changesMem->Synchronize()); - } - self::$encoder->endTag(); - self::$topCollector->AnnounceInformation(sprintf("Outgoing %d folders",$changeCount), true); - - // everything fine, save the sync state for the next time - if ($synckey == $newsynckey) - self::$deviceManager->GetStateManager()->SetSyncState($newsynckey, $newsyncstate); - } - } - self::$encoder->endTag(); - - return true; - } -} diff --git a/sources/lib/request/getattachment.php b/sources/lib/request/getattachment.php deleted file mode 100644 index 39679eb..0000000 --- a/sources/lib/request/getattachment.php +++ /dev/null @@ -1,84 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class GetAttachment extends RequestProcessor { - - /** - * Handles the GetAttachment command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $attname = Request::GetGETAttachmentName(); - if(!$attname) - return false; - - try { - $attachment = self::$backend->GetAttachmentData($attname); - $stream = $attachment->data; - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetAttachment(): attachment stream from backend: %s", $stream)); - - if ($stream == null) - throw new StatusException(sprintf("HandleGetAttachment(): No stream resource returned by backend for attachment: %s", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - if (!is_resource($stream)) - throw new StatusException(sprintf("HandleGetAttachment(): is_resource(stream) == false for attachment: %s", $attname), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); - - header("Content-Type: application/octet-stream"); - $l = fpassthru($stream); - fclose($stream); - if ($l === false) - throw new Exception("HandleGetAttachment(): fpassthru === false !!!"); - self::$topCollector->AnnounceInformation(sprintf("Streamed %d KB attachment", round($l/1024)), true); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetAttachment(): attachment with %d KB sent to mobile", round($l/1024))); - } - catch (StatusException $s) { - // StatusException already logged so we just need to pass it upwards to send a HTTP error - throw new HTTPReturnCodeException($s->getMessage(), HTTP_CODE_500, null, LOGLEVEL_DEBUG); - } - - return true; - } -} diff --git a/sources/lib/request/gethierarchy.php b/sources/lib/request/gethierarchy.php deleted file mode 100644 index c081cf8..0000000 --- a/sources/lib/request/gethierarchy.php +++ /dev/null @@ -1,80 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class GetHierarchy extends RequestProcessor { - - /** - * Handles the GetHierarchy command - * simply returns current hierarchy of all folders - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - try { - $folders = self::$backend->GetHierarchy(); - if (!$folders || empty($folders)) - throw new StatusException("GetHierarchy() did not return any data."); - - // TODO execute $data->Check() to see if SyncObject is valid - - } - catch (StatusException $ex) { - return false; - } - - self::$encoder->StartWBXML(); - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDERS); - foreach ($folders as $folder) { - self::$encoder->startTag(SYNC_FOLDERHIERARCHY_FOLDER); - $folder->Encode(self::$encoder); - self::$encoder->endTag(); - } - self::$encoder->endTag(); - - // save hierarchy for upcoming syncing - return self::$deviceManager->InitializeFolderCache($folders); - } -} diff --git a/sources/lib/request/getitemestimate.php b/sources/lib/request/getitemestimate.php deleted file mode 100644 index 683d481..0000000 --- a/sources/lib/request/getitemestimate.php +++ /dev/null @@ -1,286 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class GetItemEstimate extends RequestProcessor { - - /** - * Handles the GetItemEstimate command - * Returns an estimation of how many items will be synchronized at the next sync - * This is mostly used to show something in the progress bar - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $sc = new SyncCollections(); - - if(!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERS)) - return false; - - while(self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDER)) { - $spa = new SyncParameters(); - $spastatus = false; - - // read the folder properties - while (1) { - if(self::$decoder->getElementStartTag(SYNC_SYNCKEY)) { - try { - $spa->SetSyncKey(self::$decoder->getElementContent()); - } - catch (StateInvalidException $siex) { - $spastatus = SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED; - } - - if(!self::$decoder->getElementEndTag()) - return false; - } - - elseif(self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERID)) { - $spa->SetFolderId( self::$decoder->getElementContent()); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - // conversation mode requested - elseif(self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) { - $spa->SetConversationMode(true); - if(($conversationmode = self::$decoder->getElementContent()) !== false) { - $spa->SetConversationMode((bool)$conversationmode); - if(!self::$decoder->getElementEndTag()) - return false; - } - } - - // get items estimate does not necessarily send the folder type - elseif(self::$decoder->getElementStartTag(SYNC_GETITEMESTIMATE_FOLDERTYPE)) { - $spa->SetContentClass(self::$decoder->getElementContent()); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - //TODO AS 2.5 and filtertype not set - elseif(self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { - $spa->SetFilterType(self::$decoder->getElementContent()); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - while(self::$decoder->getElementStartTag(SYNC_OPTIONS)) { - while(1) { - $firstOption = true; - // foldertype definition - if(self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { - $foldertype = self::$decoder->getElementContent(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleGetItemEstimate(): specified options block with foldertype '%s'", $foldertype)); - - // switch the foldertype for the next options - $spa->UseCPO($foldertype); - - // set to synchronize all changes. The mobile could overwrite this value - $spa->SetFilterType(SYNC_FILTERTYPE_ALL); - - if(!self::$decoder->getElementEndTag()) - return false; - } - // if no foldertype is defined, use default cpo - else if ($firstOption){ - $spa->UseCPO(); - // set to synchronize all changes. The mobile could overwrite this value - $spa->SetFilterType(SYNC_FILTERTYPE_ALL); - } - $firstOption = false; - - if(self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { - $spa->SetFilterType(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_MAXITEMS)) { - $spa->SetWindowSize($maxitems = self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); //SYNC_GETITEMESTIMATE_FOLDER - break; - } - } - // Process folder data - - //In AS 14 request only collectionid is sent, without class - if (! $spa->HasContentClass() && $spa->HasFolderId()) { - try { - $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId())); - } - catch (NoHierarchyCacheAvailableException $nhca) { - $spastatus = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; - } - } - - // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy() - if (! $spa->HasFolderId() && $spa->HasContentClass()) { - $spa->SetFolderId(self::$deviceManager->GetFolderIdFromCacheByClass($spa->GetContentClass())); - } - - // Add collection to SC and load state - $sc->AddCollection($spa); - if ($spastatus) { - // the CPO has a folder id now, so we can set the status - $sc->AddParameter($spa, "status", $spastatus); - } - else { - try { - $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey())); - - // if this is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) - throw new StatusException(sprintf("HandleGetItemEstimate() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); - } - catch (StateNotFoundException $snfex) { - // ok, the key is invalid. Question is, if the hierarchycache is still ok - //if not, we have to issue SYNC_GETITEMESTSTATUS_COLLECTIONINVALID which triggers a FolderSync - try { - self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId()); - // we got here, so the HierarchyCache is ok - $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCKKEYINVALID); - } - catch (NoHierarchyCacheAvailableException $nhca) { - $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); - } - - self::$topCollector->AnnounceInformation("StateNotFoundException ". $sc->GetParameter($spa, "status"), true); - } - catch (StatusException $stex) { - if ($stex->getCode() == SYNC_GETITEMESTSTATUS_COLLECTIONINVALID) - $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_COLLECTIONINVALID); - else - $sc->AddParameter($spa, "status", SYNC_GETITEMESTSTATUS_SYNCSTATENOTPRIMED); - self::$topCollector->AnnounceInformation("StatusException ". $sc->GetParameter($spa, "status"), true); - } - } - - } - if(!self::$decoder->getElementEndTag()) - return false; //SYNC_GETITEMESTIMATE_FOLDERS - - if(!self::$decoder->getElementEndTag()) - return false; //SYNC_GETITEMESTIMATE_GETITEMESTIMATE - - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE); - { - $status = SYNC_GETITEMESTSTATUS_SUCCESS; - // look for changes in all collections - - try { - $sc->CountChanges(); - } - catch (StatusException $ste) { - $status = SYNC_GETITEMESTSTATUS_COLLECTIONINVALID; - } - $changes = $sc->GetChangedFolderIds(); - - foreach($sc as $folderid => $spa) { - self::$encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE); - { - if ($sc->GetParameter($spa, "status")) - $status = $sc->GetParameter($spa, "status"); - - self::$encoder->startTag(SYNC_GETITEMESTIMATE_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER); - { - self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE); - self::$encoder->content($spa->GetContentClass()); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID); - self::$encoder->content($spa->GetFolderId()); - self::$encoder->endTag(); - - if (isset($changes[$folderid]) && $changes[$folderid] !== false) { - self::$encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE); - self::$encoder->content($changes[$folderid]); - self::$encoder->endTag(); - - if ($changes[$folderid] > 0) - self::$topCollector->AnnounceInformation(sprintf("%s %d changes", $spa->GetContentClass(), $changes[$folderid]), true); - - // update the device data to mark folders as complete when synching with WM - if ($changes[$folderid] == 0) - self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED); - } - } - self::$encoder->endTag(); - } - self::$encoder->endTag(); - } - if (array_sum($changes) == 0) - self::$topCollector->AnnounceInformation("No changes found", true); - } - self::$encoder->endTag(); - - return true; - } -} diff --git a/sources/lib/request/itemoperations.php b/sources/lib/request/itemoperations.php deleted file mode 100644 index 66f519b..0000000 --- a/sources/lib/request/itemoperations.php +++ /dev/null @@ -1,389 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ItemOperations extends RequestProcessor { - - /** - * Handles the ItemOperations command - * Provides batched online handling for Fetch, EmptyFolderContents and Move - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - // Parse input - if(!self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_ITEMOPERATIONS)) - return false; - - $itemoperations = array(); - //ItemOperations can either be Fetch, EmptyFolderContents or Move - while (1) { - //TODO check if multiple item operations are possible in one request - $el = self::$decoder->getElement(); - - if($el[EN_TYPE] != EN_TYPE_STARTTAG) - return false; - - $fetch = $efc = $move = false; - $operation = array(); - if($el[EN_TAG] == SYNC_ITEMOPERATIONS_FETCH) { - $fetch = true; - $operation['operation'] = SYNC_ITEMOPERATIONS_FETCH; - self::$topCollector->AnnounceInformation("Fetch", true); - } - else if($el[EN_TAG] == SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS) { - $efc = true; - $operation['operation'] = SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS; - self::$topCollector->AnnounceInformation("Empty Folder", true); - } - else if($el[EN_TAG] == SYNC_ITEMOPERATIONS_MOVE) { - $move = true; - $operation['operation'] = SYNC_ITEMOPERATIONS_MOVE; - self::$topCollector->AnnounceInformation("Move", true); - } - - if(!$fetch && !$efc && !$move) { - ZLog::Write(LOGLEVEL_DEBUG, "Unknown item operation:".print_r($el, 1)); - self::$topCollector->AnnounceInformation("Unknown operation", true); - return false; - } - - // process operation - while(1) { - if ($fetch) { - if(self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_STORE)) { - $operation['store'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_ITEMOPERATIONS_STORE - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_LONGID)) { - $operation['longid'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_SEARCH_LONGID - } - - if(self::$decoder->getElementStartTag(SYNC_FOLDERID)) { - $operation['folderid'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_FOLDERID - } - - if(self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) { - $operation['serverid'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_SERVERENTRYID - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_FILEREFERENCE)) { - $operation['filereference'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_AIRSYNCBASE_FILEREFERENCE - } - - if(($el = self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_OPTIONS)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) { - //TODO other options - //schema - //range - //username - //password - //bodypartpreference - //rm:RightsManagementSupport - - // Save all OPTIONS into a ContentParameters object - $operation["cpo"] = new ContentParameters(); - while(1) { - while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) { - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) { - $bptype = self::$decoder->getElementContent(); - $operation["cpo"]->BodyPreference($bptype); - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) { - $operation["cpo"]->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) { - $operation["cpo"]->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) { - $operation["cpo"]->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_AIRSYNCBASE_BODYPREFERENCE - } - - if(self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) { - $operation["cpo"]->SetMimeSupport(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_RANGE)) { - $operation["range"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_SCHEMA)) { - // read schema tags - while (1) { - // TODO save elements - $el = self::$decoder->getElement(); - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - } - - //break if it reached the endtag - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - } - } // end if fetch - - if ($efc) { - if(self::$decoder->getElementStartTag(SYNC_FOLDERID)) { - $operation['folderid'] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false;//SYNC_FOLDERID - } - if(self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_OPTIONS)) { - if(self::$decoder->getElementStartTag(SYNC_ITEMOPERATIONS_DELETESUBFOLDERS)) { - $operation['deletesubfolders'] = true; - if (($dsf = self::$decoder->getElementContent()) !== false) { - $operation['deletesubfolders'] = (bool)$dsf; - if(!self::$decoder->getElementEndTag()) - return false; - } - } - self::$decoder->getElementEndTag(); - } - } - - //TODO move - - //break if it reached the endtag SYNC_ITEMOPERATIONS_FETCH or SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS or SYNC_ITEMOPERATIONS_MOVE - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } // end while operation - - $itemoperations[] = $operation; - //break if it reached the endtag - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); //SYNC_ITEMOPERATIONS_ITEMOPERATIONS - break; - } - } // end operations loop - - $status = SYNC_ITEMOPERATIONSSTATUS_SUCCESS; - - self::$encoder->startWBXML(); - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_ITEMOPERATIONS); - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS); - self::$encoder->content($status); - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_STATUS - - // Stop here if something went wrong - if ($status != SYNC_ITEMOPERATIONSSTATUS_SUCCESS) { - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_ITEMOPERATIONS - return true; - } - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_RESPONSE); - - foreach ($itemoperations as $operation) { - // fetch response - if ($operation['operation'] == SYNC_ITEMOPERATIONS_FETCH) { - - $status = SYNC_ITEMOPERATIONSSTATUS_SUCCESS; - - // retrieve the data - // Fetch throws Sync status codes, - GetAttachmentData ItemOperations codes - if (isset($operation['filereference'])) { - try { - self::$topCollector->AnnounceInformation("Get attachment data from backend with file reference"); - $data = self::$backend->GetAttachmentData($operation['filereference']); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - } - else { - try { - if (isset($operation['folderid']) && isset($operation['serverid'])) { - self::$topCollector->AnnounceInformation("Fetching data from backend with item and folder id"); - $data = self::$backend->Fetch($operation['folderid'], $operation['serverid'], $operation["cpo"]); - } - else if (isset($operation['longid'])) { - self::$topCollector->AnnounceInformation("Fetching data from backend with long id"); - $tmp = explode(":", $operation['longid']); - $data = self::$backend->Fetch($tmp[0], $tmp[1], $operation["cpo"]); - } - } - catch (StatusException $stex) { - // the only option to return is that we could not retrieve it - $status = SYNC_ITEMOPERATIONSSTATUS_CONVERSIONFAILED; - } - } - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_FETCH); - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS); - self::$encoder->content($status); - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_STATUS - - if (isset($operation['folderid']) && isset($operation['serverid'])) { - self::$encoder->startTag(SYNC_FOLDERID); - self::$encoder->content($operation['folderid']); - self::$encoder->endTag(); // end SYNC_FOLDERID - - self::$encoder->startTag(SYNC_SERVERENTRYID); - self::$encoder->content($operation['serverid']); - self::$encoder->endTag(); // end SYNC_SERVERENTRYID - - self::$encoder->startTag(SYNC_FOLDERTYPE); - self::$encoder->content("Email"); - self::$encoder->endTag(); - } - - if (isset($operation['longid'])) { - self::$encoder->startTag(SYNC_SEARCH_LONGID); - self::$encoder->content($operation['longid']); - self::$encoder->endTag(); // end SYNC_FOLDERID - - self::$encoder->startTag(SYNC_FOLDERTYPE); - self::$encoder->content("Email"); - self::$encoder->endTag(); - } - - if (isset($operation['filereference'])) { - self::$encoder->startTag(SYNC_AIRSYNCBASE_FILEREFERENCE); - self::$encoder->content($operation['filereference']); - self::$encoder->endTag(); // end SYNC_AIRSYNCBASE_FILEREFERENCE - } - - if (isset($data)) { - if (!is_object($data)) - throw new StatusException("ItemOperations->Handle(): data isn't an object !!!"); - - self::$topCollector->AnnounceInformation("Streaming data"); - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_PROPERTIES); - $data->Encode(self::$encoder); - self::$encoder->endTag(); //SYNC_ITEMOPERATIONS_PROPERTIES - } - - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_FETCH - } - // empty folder contents operation - else if ($operation['operation'] == SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS) { - try { - self::$topCollector->AnnounceInformation("Emptying folder"); - - // send request to backend - self::$backend->EmptyFolder($operation['folderid'], $operation['deletesubfolders']); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS); - - self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS); - self::$encoder->content($status); - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_STATUS - - if (isset($operation['folderid'])) { - self::$encoder->startTag(SYNC_FOLDERID); - self::$encoder->content($operation['folderid']); - self::$encoder->endTag(); // end SYNC_FOLDERID - } - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_EMPTYFOLDERCONTENTS - } - // TODO implement ItemOperations Move - // move operation - else { - self::$topCollector->AnnounceInformation("not implemented", true); - - // reply with "can't do" - self::$encoder->startTag(SYNC_ITEMOPERATIONS_MOVE); - self::$encoder->startTag(SYNC_ITEMOPERATIONS_STATUS); - self::$encoder->content(SYNC_ITEMOPERATIONSSTATUS_SERVERERROR); - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_STATUS - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_MOVE - } - - } - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_RESPONSE - self::$encoder->endTag();//SYNC_ITEMOPERATIONS_ITEMOPERATIONS - - return true; - } -} diff --git a/sources/lib/request/meetingresponse.php b/sources/lib/request/meetingresponse.php deleted file mode 100644 index 68de1e3..0000000 --- a/sources/lib/request/meetingresponse.php +++ /dev/null @@ -1,131 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class MeetingResponse extends RequestProcessor { - - /** - * Handles the MeetingResponse command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $requests = Array(); - - if(!self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE)) - return false; - - while(self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUEST)) { - $req = Array(); - while(1) { - if(self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_USERRESPONSE)) { - $req["response"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_FOLDERID)) { - $req["folderid"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_MEETINGRESPONSE_REQUESTID)) { - $req["requestid"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - return false; - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - array_push($requests, $req); - } - - if(!self::$decoder->getElementEndTag()) - return false; - - // output the error code, plus the ID of the calendar item that was generated by the - // accept of the meeting response - self::$encoder->StartWBXML(); - self::$encoder->startTag(SYNC_MEETINGRESPONSE_MEETINGRESPONSE); - - foreach($requests as $req) { - $status = SYNC_MEETRESPSTATUS_SUCCESS; - - try { - $calendarid = self::$backend->MeetingResponse($req["requestid"], $req["folderid"], $req["response"]); - if ($calendarid === false) - throw new StatusException("HandleMeetingResponse() not possible", SYNC_MEETRESPSTATUS_SERVERERROR); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - self::$encoder->startTag(SYNC_MEETINGRESPONSE_RESULT); - self::$encoder->startTag(SYNC_MEETINGRESPONSE_REQUESTID); - self::$encoder->content($req["requestid"]); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_MEETINGRESPONSE_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - if($status == SYNC_MEETRESPSTATUS_SUCCESS && !empty($calendarid)) { - self::$encoder->startTag(SYNC_MEETINGRESPONSE_CALENDARID); - self::$encoder->content($calendarid); - self::$encoder->endTag(); - } - self::$encoder->endTag(); - self::$topCollector->AnnounceInformation(sprintf("Operation status %d", $status), true); - } - self::$encoder->endTag(); - - return true; - } -} diff --git a/sources/lib/request/moveitems.php b/sources/lib/request/moveitems.php deleted file mode 100644 index b2a028d..0000000 --- a/sources/lib/request/moveitems.php +++ /dev/null @@ -1,137 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class MoveItems extends RequestProcessor { - - /** - * Handles the MoveItems command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - if(!self::$decoder->getElementStartTag(SYNC_MOVE_MOVES)) - return false; - - $moves = array(); - while(self::$decoder->getElementStartTag(SYNC_MOVE_MOVE)) { - $move = array(); - if(self::$decoder->getElementStartTag(SYNC_MOVE_SRCMSGID)) { - $move["srcmsgid"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - break; - } - if(self::$decoder->getElementStartTag(SYNC_MOVE_SRCFLDID)) { - $move["srcfldid"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - break; - } - if(self::$decoder->getElementStartTag(SYNC_MOVE_DSTFLDID)) { - $move["dstfldid"] = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) - break; - } - array_push($moves, $move); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(!self::$decoder->getElementEndTag()) - return false; - - self::$encoder->StartWBXML(); - - self::$encoder->startTag(SYNC_MOVE_MOVES); - - foreach($moves as $move) { - self::$encoder->startTag(SYNC_MOVE_RESPONSE); - self::$encoder->startTag(SYNC_MOVE_SRCMSGID); - self::$encoder->content($move["srcmsgid"]); - self::$encoder->endTag(); - - $status = SYNC_MOVEITEMSSTATUS_SUCCESS; - $result = false; - try { - // if the source folder is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($move["srcfldid"]))) - throw new StatusException(sprintf("HandleMoveItems() could not Setup() the backend for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - $importer = self::$backend->GetImporter($move["srcfldid"]); - if ($importer === false) - throw new StatusException(sprintf("HandleMoveItems() could not get an importer for folder id '%s'", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - - // get saved SyncParameters for this folder - $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($move["srcfldid"]); - if (!$spa->HasSyncKey()) - throw new StatusException(sprintf("MoveItems(): Source folder id '%s' is not fully synchronized. Unable to perform operation.", $move["srcfldid"]), SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID); - $importer->ConfigContentParameters($spa->GetCPO()); - - $result = $importer->ImportMessageMove($move["srcmsgid"], $move["dstfldid"]); - // We discard the importer state for now. - } - catch (StatusException $stex) { - if ($stex->getCode() == SYNC_STATUS_FOLDERHIERARCHYCHANGED) // same as SYNC_FSSTATUS_CODEUNKNOWN - $status = SYNC_MOVEITEMSSTATUS_INVALIDSOURCEID; - else - $status = $stex->getCode(); - } - - self::$topCollector->AnnounceInformation(sprintf("Operation status: %s", $status), true); - - self::$encoder->startTag(SYNC_MOVE_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_MOVE_DSTMSGID); - self::$encoder->content( (($result !== false ) ? $result : $move["srcmsgid"])); - self::$encoder->endTag(); - self::$encoder->endTag(); - } - - self::$encoder->endTag(); - return true; - } -} diff --git a/sources/lib/request/notify.php b/sources/lib/request/notify.php deleted file mode 100644 index 0337f6a..0000000 --- a/sources/lib/request/notify.php +++ /dev/null @@ -1,82 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Notify extends RequestProcessor { - - /** - * Handles the Notify command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - if(!self::$decoder->getElementStartTag(SYNC_AIRNOTIFY_NOTIFY)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_AIRNOTIFY_DEVICEINFO)) - return false; - - if(!self::$decoder->getElementEndTag()) - return false; - - if(!self::$decoder->getElementEndTag()) - return false; - - self::$encoder->StartWBXML(); - - self::$encoder->startTag(SYNC_AIRNOTIFY_NOTIFY); - { - self::$encoder->startTag(SYNC_AIRNOTIFY_STATUS); - self::$encoder->content(1); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_AIRNOTIFY_VALIDCARRIERPROFILES); - self::$encoder->endTag(); - } - self::$encoder->endTag(); - - return true; - } -} diff --git a/sources/lib/request/ping.php b/sources/lib/request/ping.php deleted file mode 100644 index 8d4a071..0000000 --- a/sources/lib/request/ping.php +++ /dev/null @@ -1,227 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Ping extends RequestProcessor { - - /** - * Handles the Ping command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $interval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 30; - $pingstatus = false; - $fakechanges = array(); - $foundchanges = false; - - // Contains all requested folders (containers) - $sc = new SyncCollections(); - - // read from stream to see if the symc params are being sent - $params_present = self::$decoder->getElementStartTag(SYNC_PING_PING); - - // Load all collections - do load states and check permissions - try { - $sc->LoadAllCollections(true, true, true); - } - catch (StateNotFoundException $snfex) { - // if no params are present, indicate to send params, else do hierarchy sync - if (!$params_present) { - $pingstatus = SYNC_PINGSTATUS_FAILINGPARAMS; - self::$topCollector->AnnounceInformation("StateNotFoundException: require PingParameters", true); - } - else { - $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED; - self::$topCollector->AnnounceInformation("StateNotFoundException: require HierarchySync", true); - } - } - catch (StateInvalidException $snfex) { - // we do not have a ping status for this, but SyncCollections should have generated fake changes for the folders which are broken - $fakechanges = $sc->GetChangedFolderIds(); - $foundchanges = true; - - self::$topCollector->AnnounceInformation("StateInvalidException: force sync", true); - } - catch (StatusException $stex) { - $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED; - self::$topCollector->AnnounceInformation("StatusException: require HierarchySync", true); - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandlePing(): reference PolicyKey for PING: %s", $sc->GetReferencePolicyKey())); - - // receive PING initialization data - if($params_present) { - self::$topCollector->AnnounceInformation("Processing PING data"); - ZLog::Write(LOGLEVEL_DEBUG, "HandlePing(): initialization data received"); - - if(self::$decoder->getElementStartTag(SYNC_PING_LIFETIME)) { - $sc->SetLifetime(self::$decoder->getElementContent()); - self::$decoder->getElementEndTag(); - } - - if(($el = self::$decoder->getElementStartTag(SYNC_PING_FOLDERS)) && $el[EN_FLAGS] & EN_FLAGS_CONTENT) { - // remove PingableFlag from all collections - foreach ($sc as $folderid => $spa) - $spa->DelPingableFlag(); - - while(self::$decoder->getElementStartTag(SYNC_PING_FOLDER)) { - while(1) { - if(self::$decoder->getElementStartTag(SYNC_PING_SERVERENTRYID)) { - $folderid = self::$decoder->getElementContent(); - self::$decoder->getElementEndTag(); - } - if(self::$decoder->getElementStartTag(SYNC_PING_FOLDERTYPE)) { - $class = self::$decoder->getElementContent(); - self::$decoder->getElementEndTag(); - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - - $spa = $sc->GetCollection($folderid); - if (! $spa) { - // The requested collection is not synchronized. - // check if the HierarchyCache is available, if not, trigger a HierarchySync - try { - self::$deviceManager->GetFolderClassFromCacheByID($folderid); - } - catch (NoHierarchyCacheAvailableException $nhca) { - ZLog::Write(LOGLEVEL_INFO, sprintf("HandlePing(): unknown collection '%s', triggering HierarchySync", $folderid)); - $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED; - } - - // Trigger a Sync request because then the device will be forced to resync this folder. - $fakechanges[$folderid] = 1; - $foundchanges = true; - } - else if ($class == $spa->GetContentClass()) { - $spa->SetPingableFlag(true); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandlePing(): using saved sync state for '%s' id '%s'", $spa->GetContentClass(), $folderid)); - } - - } - if(!self::$decoder->getElementEndTag()) - return false; - } - if(!self::$decoder->getElementEndTag()) - return false; - - // save changed data - foreach ($sc as $folderid => $spa) - $sc->SaveCollection($spa); - } // END SYNC_PING_PING - else { - // if no ping initialization data was sent, we check if we have pingable folders - // if not, we indicate that there is nothing to do. - if (! $sc->PingableFolders()) { - $pingstatus = SYNC_PINGSTATUS_FAILINGPARAMS; - ZLog::Write(LOGLEVEL_DEBUG, "HandlePing(): no pingable folders found and no initialization data sent. Returning SYNC_PINGSTATUS_FAILINGPARAMS."); - } - } - - // Check for changes on the default LifeTime, set interval and ONLY on pingable collections - try { - if (!$pingstatus && empty($fakechanges)) { - $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval, true); - } - } - catch (StatusException $ste) { - switch($ste->getCode()) { - case SyncCollections::ERROR_NO_COLLECTIONS: - $pingstatus = SYNC_PINGSTATUS_FAILINGPARAMS; - break; - case SyncCollections::ERROR_WRONG_HIERARCHY: - $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED; - self::$deviceManager->AnnounceProcessStatus(false, $pingstatus); - break; - case SyncCollections::OBSOLETE_CONNECTION: - $foundchanges = false; - break; - case SyncCollections::HIERARCHY_CHANGED: - $pingstatus = SYNC_PINGSTATUS_FOLDERHIERSYNCREQUIRED; - break; - } - } - - self::$encoder->StartWBXML(); - self::$encoder->startTag(SYNC_PING_PING); - { - self::$encoder->startTag(SYNC_PING_STATUS); - if (isset($pingstatus) && $pingstatus) - self::$encoder->content($pingstatus); - else - self::$encoder->content($foundchanges ? SYNC_PINGSTATUS_CHANGES : SYNC_PINGSTATUS_HBEXPIRED); - self::$encoder->endTag(); - - if (! $pingstatus) { - self::$encoder->startTag(SYNC_PING_FOLDERS); - - if (empty($fakechanges)) - $changes = $sc->GetChangedFolderIds(); - else - $changes = $fakechanges; - - foreach ($changes as $folderid => $changecount) { - if ($changecount > 0) { - self::$encoder->startTag(SYNC_PING_FOLDER); - self::$encoder->content($folderid); - self::$encoder->endTag(); - if (empty($fakechanges)) - self::$topCollector->AnnounceInformation(sprintf("Found change in %s", $sc->GetCollection($folderid)->GetContentClass()), true); - } - } - self::$encoder->endTag(); - } - } - self::$encoder->endTag(); - - return true; - } -} diff --git a/sources/lib/request/provisioning.php b/sources/lib/request/provisioning.php deleted file mode 100644 index 5bff21e..0000000 --- a/sources/lib/request/provisioning.php +++ /dev/null @@ -1,229 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Provisioning extends RequestProcessor { - - /** - * Handles the Provisioning command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $status = SYNC_PROVISION_STATUS_SUCCESS; - $policystatus = SYNC_PROVISION_POLICYSTATUS_SUCCESS; - - $rwstatus = self::$deviceManager->GetProvisioningWipeStatus(); - $rwstatusWiped = false; - - // if this is a regular provisioning require that an authenticated remote user - if ($rwstatus < SYNC_PROVISION_RWSTATUS_PENDING) { - ZLog::Write(LOGLEVEL_DEBUG, "RequestProcessor::HandleProvision(): Forcing delayed Authentication"); - self::Authenticate(); - } - - $phase2 = true; - - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_PROVISION)) - return false; - - //handle android remote wipe. - if (self::$decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) { - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_STATUS)) - return false; - - $instatus = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - - if(!self::$decoder->getElementEndTag()) - return false; - - $phase2 = false; - $rwstatusWiped = true; - } - else { - - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_POLICIES)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_POLICY)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_POLICYTYPE)) - return false; - - $policytype = self::$decoder->getElementContent(); - if ($policytype != 'MS-WAP-Provisioning-XML' && $policytype != 'MS-EAS-Provisioning-WBXML') { - $status = SYNC_PROVISION_STATUS_SERVERERROR; - } - if(!self::$decoder->getElementEndTag()) //policytype - return false; - - if (self::$decoder->getElementStartTag(SYNC_PROVISION_POLICYKEY)) { - $devpolicykey = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_STATUS)) - return false; - - $instatus = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - - $phase2 = false; - } - - if(!self::$decoder->getElementEndTag()) //policy - return false; - - if(!self::$decoder->getElementEndTag()) //policies - return false; - - if (self::$decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) { - if(!self::$decoder->getElementStartTag(SYNC_PROVISION_STATUS)) - return false; - - $status = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - - if(!self::$decoder->getElementEndTag()) - return false; - - $rwstatusWiped = true; - } - } - if(!self::$decoder->getElementEndTag()) //provision - return false; - - if (PROVISIONING !== true) { - ZLog::Write(LOGLEVEL_INFO, "No policies deployed to device"); - $policystatus = SYNC_PROVISION_POLICYSTATUS_NOPOLICY; - } - - self::$encoder->StartWBXML(); - - //set the new final policy key in the device manager - // START ADDED dw2412 Android provisioning fix - if (!$phase2) { - $policykey = self::$deviceManager->GenerateProvisioningPolicyKey(); - self::$deviceManager->SetProvisioningPolicyKey($policykey); - self::$topCollector->AnnounceInformation("Policies deployed", true); - } - else { - // just create a temporary key (i.e. iPhone OS4 Beta does not like policykey 0 in response) - $policykey = self::$deviceManager->GenerateProvisioningPolicyKey(); - } - // END ADDED dw2412 Android provisioning fix - - self::$encoder->startTag(SYNC_PROVISION_PROVISION); - { - self::$encoder->startTag(SYNC_PROVISION_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_PROVISION_POLICIES); - self::$encoder->startTag(SYNC_PROVISION_POLICY); - - if(isset($policytype)) { - self::$encoder->startTag(SYNC_PROVISION_POLICYTYPE); - self::$encoder->content($policytype); - self::$encoder->endTag(); - } - - self::$encoder->startTag(SYNC_PROVISION_STATUS); - self::$encoder->content($policystatus); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_PROVISION_POLICYKEY); - self::$encoder->content($policykey); - self::$encoder->endTag(); - - if ($phase2 && $policystatus === SYNC_PROVISION_POLICYSTATUS_SUCCESS) { - self::$encoder->startTag(SYNC_PROVISION_DATA); - if ($policytype == 'MS-WAP-Provisioning-XML') { - self::$encoder->content(''); - } - elseif ($policytype == 'MS-EAS-Provisioning-WBXML') { - self::$encoder->startTag(SYNC_PROVISION_EASPROVISIONDOC); - - $prov = self::$deviceManager->GetProvisioningObject(); - if (!$prov->Check()) - throw new FatalException("Invalid policies!"); - - $prov->Encode(self::$encoder); - self::$encoder->endTag(); - } - else { - ZLog::Write(LOGLEVEL_WARN, "Wrong policy type"); - self::$topCollector->AnnounceInformation("Policytype not supported", true); - return false; - } - self::$topCollector->AnnounceInformation("Updated provisiong", true); - - self::$encoder->endTag();//data - } - self::$encoder->endTag();//policy - self::$encoder->endTag(); //policies - } - - //wipe data if a higher RWSTATUS is requested - if ($rwstatus > SYNC_PROVISION_RWSTATUS_OK && $policystatus === SYNC_PROVISION_POLICYSTATUS_SUCCESS) { - self::$encoder->startTag(SYNC_PROVISION_REMOTEWIPE, false, true); - self::$deviceManager->SetProvisioningWipeStatus(($rwstatusWiped)?SYNC_PROVISION_RWSTATUS_WIPED:SYNC_PROVISION_RWSTATUS_REQUESTED); - self::$topCollector->AnnounceInformation(sprintf("Remote wipe %s", ($rwstatusWiped)?"executed":"requested"), true); - } - - self::$encoder->endTag();//provision - - return true; - } -} diff --git a/sources/lib/request/request.php b/sources/lib/request/request.php deleted file mode 100644 index 8954453..0000000 --- a/sources/lib/request/request.php +++ /dev/null @@ -1,625 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Request { - const UNKNOWN = "unknown"; - - /** - * self::filterEvilInput() options - */ - const LETTERS_ONLY = 1; - const HEX_ONLY = 2; - const WORDCHAR_ONLY = 3; - const NUMBERS_ONLY = 4; - const NUMBERSDOT_ONLY = 5; - const HEX_EXTENDED = 6; - - /** - * Command parameters for base64 encoded requests (AS >= 12.1) - */ - const COMMANDPARAM_ATTACHMENTNAME = 0; - const COMMANDPARAM_COLLECTIONID = 1; //deprecated - const COMMANDPARAM_COLLECTIONNAME = 2; //deprecated - const COMMANDPARAM_ITEMID = 3; - const COMMANDPARAM_LONGID = 4; - const COMMANDPARAM_PARENTID = 5; //deprecated - const COMMANDPARAM_OCCURRENCE = 6; - const COMMANDPARAM_OPTIONS = 7; //used by SmartReply, SmartForward, SendMail, ItemOperations - const COMMANDPARAM_USER = 8; //used by any command - //possible bitflags for COMMANDPARAM_OPTIONS - const COMMANDPARAM_OPTIONS_SAVEINSENT = 0x01; - const COMMANDPARAM_OPTIONS_ACCEPTMULTIPART = 0x02; - - static private $input; - static private $output; - static private $getparameters; - static private $command; - static private $device; - static private $method; - static private $remoteAddr; - static private $getUser; - static private $devid; - static private $devtype; - static private $authUser; - static private $authDomain; - static private $authPassword; - static private $asProtocolVersion; - static private $policykey; - static private $useragent; - static private $attachmentName; - static private $collectionId; - static private $itemId; - static private $longId; //TODO - static private $occurence; //TODO - static private $saveInSent; - static private $acceptMultipart; - - - /** - * Initializes request data - * - * @access public - * @return - */ - static public function Initialize() { - // try to open stdin & stdout - self::$input = fopen("php://input", "r"); - self::$output = fopen("php://output", "w+"); - - // Parse the standard GET parameters - if(isset($_GET["Cmd"])) - self::$command = self::filterEvilInput($_GET["Cmd"], self::LETTERS_ONLY); - - // getUser is unfiltered, as everything is allowed.. even "/", "\" or ".." - if(isset($_GET["User"])) { - self::$getUser = strtolower($_GET["User"]); - if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) { - self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); - } - } - if(isset($_GET["DeviceId"])) - self::$devid = strtolower(self::filterEvilInput($_GET["DeviceId"], self::WORDCHAR_ONLY)); - if(isset($_GET["DeviceType"])) - self::$devtype = self::filterEvilInput($_GET["DeviceType"], self::LETTERS_ONLY); - if (isset($_GET["AttachmentName"])) - self::$attachmentName = self::filterEvilInput($_GET["AttachmentName"], self::HEX_EXTENDED); - if (isset($_GET["CollectionId"])) - self::$collectionId = self::filterEvilInput($_GET["CollectionId"], self::HEX_ONLY); - if (isset($_GET["ItemId"])) - self::$itemId = self::filterEvilInput($_GET["ItemId"], self::HEX_ONLY); - if (isset($_GET["SaveInSent"]) && $_GET["SaveInSent"] == "T") - self::$saveInSent = true; - - if(isset($_SERVER["REQUEST_METHOD"])) - self::$method = self::filterEvilInput($_SERVER["REQUEST_METHOD"], self::LETTERS_ONLY); - // TODO check IPv6 addresses - if(isset($_SERVER["REMOTE_ADDR"])) - self::$remoteAddr = self::filterEvilInput($_SERVER["REMOTE_ADDR"], self::NUMBERSDOT_ONLY); - - // in protocol version > 14 mobile send these inputs as encoded query string - if (!isset(self::$command) && !empty($_SERVER['QUERY_STRING']) && Utils::IsBase64String($_SERVER['QUERY_STRING'])) { - $query = Utils::DecodeBase64URI($_SERVER['QUERY_STRING']); - if (!isset(self::$command) && isset($query['Command'])) - self::$command = Utils::GetCommandFromCode($query['Command']); - - if (!isset(self::$getUser) && isset($query[self::COMMANDPARAM_USER])) { - self::$getUser = strtolower($query[self::COMMANDPARAM_USER]); - if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) { - self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); - } - } - - if (!isset(self::$devid) && isset($query['DevID'])) - self::$devid = strtolower(self::filterEvilInput($query['DevID'], self::WORDCHAR_ONLY)); - - if (!isset(self::$devtype) && isset($query['DevType'])) - self::$devtype = self::filterEvilInput($query['DevType'], self::LETTERS_ONLY); - - if (isset($query['PolKey'])) - self::$policykey = (int) self::filterEvilInput($query['PolKey'], self::NUMBERS_ONLY); - - if (isset($query['ProtVer'])) - self::$asProtocolVersion = self::filterEvilInput($query['ProtVer'], self::NUMBERS_ONLY) / 10; - - if (isset($query[self::COMMANDPARAM_ATTACHMENTNAME])) - self::$attachmentName = self::filterEvilInput($query[self::COMMANDPARAM_ATTACHMENTNAME], self::HEX_EXTENDED); - - if (isset($query[self::COMMANDPARAM_COLLECTIONID])) - self::$collectionId = self::filterEvilInput($query[self::COMMANDPARAM_COLLECTIONID], self::HEX_ONLY); - - if (isset($query[self::COMMANDPARAM_ITEMID])) - self::$itemId = self::filterEvilInput($query[self::COMMANDPARAM_ITEMID], self::HEX_ONLY); - - if (isset($query[self::COMMANDPARAM_OPTIONS]) && (ord($query[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_SAVEINSENT)) - self::$saveInSent = true; - - if (isset($query[self::COMMANDPARAM_OPTIONS]) && (ord($query[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_ACCEPTMULTIPART)) - self::$acceptMultipart = true; - } - - // in base64 encoded query string user is not necessarily set - if (!isset(self::$getUser) && isset($_SERVER['PHP_AUTH_USER'])) { - list(self::$getUser,) = Utils::SplitDomainUser(strtolower($_SERVER['PHP_AUTH_USER'])); - if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) { - self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); - } - } - } - - /** - * Reads and processes the request headers - * - * @access public - * @return - */ - static public function ProcessHeaders() { - self::$useragent = (isset($_SERVER['HTTP_USER_AGENT']))? $_SERVER['HTTP_USER_AGENT'] : self::UNKNOWN; - if (!isset(self::$asProtocolVersion)) - self::$asProtocolVersion = (isset($_SERVER['HTTP_MS_ASPROTOCOLVERSION']))? self::filterEvilInput($_SERVER['HTTP_MS_ASPROTOCOLVERSION'], self::NUMBERSDOT_ONLY) : ZPush::GetLatestSupportedASVersion(); - - //if policykey is not yet set, try to set it from the header - //the policy key might be set in Request::Initialize from the base64 encoded query - if (!isset(self::$policykey)) { - if (isset($_SERVER['HTTP_X_MS_POLICYKEY'])) - self::$policykey = (int) self::filterEvilInput($_SERVER['HTTP_X_MS_POLICYKEY'], self::NUMBERS_ONLY); - else - self::$policykey = 0; - } - - if (!isset(self::$acceptMultipart) && isset($_SERVER['HTTP_MS_ASACCEPTMULTIPART']) && strtoupper($_SERVER['HTTP_MS_ASACCEPTMULTIPART']) == "T") { - self::$acceptMultipart = true; - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::ProcessHeaders() ASVersion: %s", self::$asProtocolVersion)); - - if (defined('USE_X_FORWARDED_FOR_HEADER') && USE_X_FORWARDED_FOR_HEADER == true && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $forwardedIP = self::filterEvilInput($_SERVER['HTTP_X_FORWARDED_FOR'], self::NUMBERSDOT_ONLY); - if ($forwardedIP) { - self::$remoteAddr = $forwardedIP; - ZLog::Write(LOGLEVEL_INFO, sprintf("'X-Forwarded-for' indicates remote IP: %s", self::$remoteAddr)); - } - } - } - - /** - * Reads and parses the HTTP-Basic-Auth data - * - * @access public - * @return boolean data sent or not - */ - static public function AuthenticationInfo() { - // split username & domain if received as one - if (isset($_SERVER['PHP_AUTH_USER'])) { - list(self::$authUser, self::$authDomain) = Utils::SplitDomainUser($_SERVER['PHP_AUTH_USER']); - self::$authPassword = (isset($_SERVER['PHP_AUTH_PW']))?$_SERVER['PHP_AUTH_PW'] : ""; - } - if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) { - self::$authUser = Utils::GetLocalPartFromEmail(self::$authUser); - } - // authUser & authPassword are unfiltered! - return (self::$authUser != "" && self::$authPassword != ""); - } - - - /**---------------------------------------------------------------------------------------------------------- - * Getter & Checker - */ - - /** - * Returns the input stream - * - * @access public - * @return handle/boolean false if not available - */ - static public function GetInputStream() { - if (isset(self::$input)) - return self::$input; - else - return false; - } - - /** - * Returns the output stream - * - * @access public - * @return handle/boolean false if not available - */ - static public function GetOutputStream() { - if (isset(self::$output)) - return self::$output; - else - return false; - } - - /** - * Returns the request method - * - * @access public - * @return string - */ - static public function GetMethod() { - if (isset(self::$method)) - return self::$method; - else - return self::UNKNOWN; - } - - /** - * Returns the value of the user parameter of the querystring - * - * @access public - * @return string/boolean false if not available - */ - static public function GetGETUser() { - if (isset(self::$getUser)) - return self::$getUser; - else - return self::UNKNOWN; - } - - /** - * Returns the value of the ItemId parameter of the querystring - * - * @access public - * @return string/boolean false if not available - */ - static public function GetGETItemId() { - if (isset(self::$itemId)) - return self::$itemId; - else - return false; - } - - /** - * Returns the value of the CollectionId parameter of the querystring - * - * @access public - * @return string/boolean false if not available - */ - static public function GetGETCollectionId() { - if (isset(self::$collectionId)) - return self::$collectionId; - else - return false; - } - - /** - * Returns if the SaveInSent parameter of the querystring is set - * - * @access public - * @return boolean - */ - static public function GetGETSaveInSent() { - if (isset(self::$saveInSent)) - return self::$saveInSent; - else - return true; - } - - /** - * Returns if the AcceptMultipart parameter of the querystring is set - * - * @access public - * @return boolean - */ - static public function GetGETAcceptMultipart() { - if (isset(self::$acceptMultipart)) - return self::$acceptMultipart; - else - return false; - } - - /** - * Returns the value of the AttachmentName parameter of the querystring - * - * @access public - * @return string/boolean false if not available - */ - static public function GetGETAttachmentName() { - if (isset(self::$attachmentName)) - return self::$attachmentName; - else - return false; - } - - /** - * Returns the authenticated user - * - * @access public - * @return string/boolean false if not available - */ - static public function GetAuthUser() { - if (isset(self::$authUser)) - return self::$authUser; - else - return false; - } - - /** - * Returns the authenticated domain for the user - * - * @access public - * @return string/boolean false if not available - */ - static public function GetAuthDomain() { - if (isset(self::$authDomain)) - return self::$authDomain; - else - return false; - } - - /** - * Returns the transmitted password - * - * @access public - * @return string/boolean false if not available - */ - static public function GetAuthPassword() { - if (isset(self::$authPassword)) - return self::$authPassword; - else - return false; - } - - /** - * Returns the RemoteAddress - * - * @access public - * @return string - */ - static public function GetRemoteAddr() { - if (isset(self::$remoteAddr)) - return self::$remoteAddr; - else - return "UNKNOWN"; - } - - /** - * Returns the command to be executed - * - * @access public - * @return string/boolean false if not available - */ - static public function GetCommand() { - if (isset(self::$command)) - return self::$command; - else - return false; - } - - /** - * Returns the command code which is being executed - * - * @access public - * @return string/boolean false if not available - */ - static public function GetCommandCode() { - if (isset(self::$command)) - return Utils::GetCodeFromCommand(self::$command); - else - return false; - } - - /** - * Returns the device id transmitted - * - * @access public - * @return string/boolean false if not available - */ - static public function GetDeviceID() { - if (isset(self::$devid)) - return self::$devid; - else - return false; - } - - /** - * Returns the device type if transmitted - * - * @access public - * @return string/boolean false if not available - */ - static public function GetDeviceType() { - if (isset(self::$devtype)) - return self::$devtype; - else - return false; - } - - /** - * Returns the value of supported AS protocol from the headers - * - * @access public - * @return string/boolean false if not available - */ - static public function GetProtocolVersion() { - if (isset(self::$asProtocolVersion)) - return self::$asProtocolVersion; - else - return false; - } - - /** - * Returns the user agent sent in the headers - * - * @access public - * @return string/boolean false if not available - */ - static public function GetUserAgent() { - if (isset(self::$useragent)) - return self::$useragent; - else - return self::UNKNOWN; - } - - /** - * Returns policy key sent by the device - * - * @access public - * @return int/boolean false if not available - */ - static public function GetPolicyKey() { - if (isset(self::$policykey)) - return self::$policykey; - else - return false; - } - - /** - * Indicates if a policy key was sent by the device - * - * @access public - * @return boolean - */ - static public function WasPolicyKeySent() { - return isset($_SERVER['HTTP_X_MS_POLICYKEY']); - } - - /** - * Indicates if Z-Push was called with a POST request - * - * @access public - * @return boolean - */ - static public function IsMethodPOST() { - return (self::$method == "POST"); - } - - /** - * Indicates if Z-Push was called with a GET request - * - * @access public - * @return boolean - */ - static public function IsMethodGET() { - return (self::$method == "GET"); - } - - /** - * Indicates if Z-Push was called with a OPTIONS request - * - * @access public - * @return boolean - */ - static public function IsMethodOPTIONS() { - return (self::$method == "OPTIONS"); - } - - /** - * Sometimes strange device ids are sumbitted - * No device information should be saved when this happens - * - * @access public - * @return boolean false if invalid - */ - static public function IsValidDeviceID() { - if (self::GetDeviceID() === "validate" || self::GetDeviceID() === "webservice") - return false; - else - return true; - } - - /** - * Returns the amount of data sent in this request (from the headers) - * - * @access public - * @return int - */ - static public function GetContentLength() { - return (isset($_SERVER['HTTP_CONTENT_LENGTH']))? (int) $_SERVER['HTTP_CONTENT_LENGTH'] : 0; - } - - /** - * Returns the amount of seconds this request is able to be kept open without the client - * closing it. This depends on the vendor. - * - * @access public - * @return boolean - */ - static public function GetExpectedConnectionTimeout() { - // Different vendors implement different connection timeouts. - // In order to optimize processing, we return a specific time for the major - // classes currently known (feedback welcome). - // The amount of time returned is somehow lower than the max timeout so we have - // time for processing. - - // Apple and Windows Phone have higher timeouts (4min = 240sec) - if (in_array(self::GetDeviceType(), array("iPod", "iPad", "iPhone", "WP"))) { - return 200; - } - // Samsung devices have a intermediate timeout (90sec) - if (in_array(self::GetDeviceType(), array("SAMSUNGGTI"))) { - return 50; - } - - // for all other devices, a timeout of 30 seconds is expected - return 20; - } - - /**---------------------------------------------------------------------------------------------------------- - * Private stuff - */ - - /** - * Replaces all not allowed characters in a string - * - * @param string $input the input string - * @param int $filter one of the predefined filters: LETTERS_ONLY, HEX_ONLY, WORDCHAR_ONLY, NUMBERS_ONLY, NUMBERSDOT_ONLY - * @param char $replacevalue (opt) a character the filtered characters should be replaced with - * - * @access public - * @return string - */ - static private function filterEvilInput($input, $filter, $replacevalue = '') { - $re = false; - if ($filter == self::LETTERS_ONLY) $re = "/[^A-Za-z]/"; - else if ($filter == self::HEX_ONLY) $re = "/[^A-Fa-f0-9]/"; - else if ($filter == self::WORDCHAR_ONLY) $re = "/[^A-Za-z0-9]/"; - else if ($filter == self::NUMBERS_ONLY) $re = "/[^0-9]/"; - else if ($filter == self::NUMBERSDOT_ONLY) $re = "/[^0-9\.]/"; - else if ($filter == self::HEX_EXTENDED) $re = "/[^A-Fa-f0-9\:]/"; - - return ($re) ? preg_replace($re, $replacevalue, $input) : ''; - } -} diff --git a/sources/lib/request/requestprocessor.php b/sources/lib/request/requestprocessor.php deleted file mode 100644 index a4d8b8c..0000000 --- a/sources/lib/request/requestprocessor.php +++ /dev/null @@ -1,168 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -abstract class RequestProcessor { - static protected $backend; - static protected $deviceManager; - static protected $topCollector; - static protected $decoder; - static protected $encoder; - static protected $userIsAuthenticated; - static protected $specialHeaders; - - /** - * Authenticates the remote user - * The sent HTTP authentication information is used to on Backend->Logon(). - * As second step the GET-User verified by Backend->Setup() for permission check - * Request::GetGETUser() is usually the same as the Request::GetAuthUser(). - * If the GETUser is different from the AuthUser, the AuthUser MUST HAVE admin - * permissions on GETUsers data store. Only then the Setup() will be sucessfull. - * This allows the user 'john' to do operations as user 'joe' if he has sufficient privileges. - * - * @access public - * @return - * @throws AuthenticationRequiredException - */ - static public function Authenticate() { - self::$userIsAuthenticated = false; - - // when a certificate is sent, allow authentication only as the certificate owner - if(defined("CERTIFICATE_OWNER_PARAMETER") && isset($_SERVER[CERTIFICATE_OWNER_PARAMETER]) && strtolower($_SERVER[CERTIFICATE_OWNER_PARAMETER]) != strtolower(Request::GetAuthUser())) - throw new AuthenticationRequiredException(sprintf("Access denied. Access is allowed only for the certificate owner '%s'", $_SERVER[CERTIFICATE_OWNER_PARAMETER])); - - $backend = ZPush::GetBackend(); - if($backend->Logon(Request::GetAuthUser(), Request::GetAuthDomain(), Request::GetAuthPassword()) == false) - throw new AuthenticationRequiredException("Access denied. Username or password incorrect"); - - // mark this request as "authenticated" - self::$userIsAuthenticated = true; - - // check Auth-User's permissions on GETUser's store - if($backend->Setup(Request::GetGETUser(), true) == false) - throw new AuthenticationRequiredException(sprintf("Not enough privileges of '%s' to setup for user '%s': Permission denied", Request::GetAuthUser(), Request::GetGETUser())); - } - - /** - * Indicates if the user was "authenticated" - * - * @access public - * @return boolean - */ - static public function isUserAuthenticated() { - if (!isset(self::$userIsAuthenticated)) - return false; - return self::$userIsAuthenticated; - } - - /** - * Initialize the RequestProcessor - * - * @access public - * @return - */ - static public function Initialize() { - self::$backend = ZPush::GetBackend(); - self::$deviceManager = ZPush::GetDeviceManager(); - self::$topCollector = ZPush::GetTopCollector(); - - if (!ZPush::CommandNeedsPlainInput(Request::GetCommandCode())) - self::$decoder = new WBXMLDecoder(Request::GetInputStream()); - - self::$encoder = new WBXMLEncoder(Request::GetOutputStream(), Request::GetGETAcceptMultipart()); - } - - /** - * Loads the command handler and processes a command sent from the mobile - * - * @access public - * @return boolean - */ - static public function HandleRequest() { - $handler = ZPush::GetRequestHandlerForCommand(Request::GetCommandCode()); - - // if there is an error decoding wbxml, consume remaining data and include it in the WBXMLException - if (!$handler->Handle(Request::GetCommandCode())) { - $wbxmlLog = "no decoder"; - if (self::$decoder) { - self::$decoder->readRemainingData(); - $wbxmlLog = self::$decoder->getWBXMLLog(); - } - throw new WBXMLException("Debug data: " . $wbxmlLog); - } - - // also log WBXML in happy case - if (self::$decoder && @constant('WBXML_DEBUG') === true) { - ZLog::Write(LOGLEVEL_WBXML, "WBXML-IN : ". self::$decoder->getWBXMLLog(), false); - } - } - - /** - * Returns any additional headers which should be sent to the mobile - * - * @access public - * @return array - */ - static public function GetSpecialHeaders() { - if (!isset(self::$specialHeaders) || !is_array(self::$specialHeaders)) - return array(); - - return self::$specialHeaders; - } - - /** - * Handles a command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - abstract public function Handle($commandCode); -} diff --git a/sources/lib/request/resolverecipients.php b/sources/lib/request/resolverecipients.php deleted file mode 100755 index 75fe9d9..0000000 --- a/sources/lib/request/resolverecipients.php +++ /dev/null @@ -1,103 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ResolveRecipients extends RequestProcessor { - - /** - * Handles the ResolveRecipients command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - // Parse input - if(!self::$decoder->getElementStartTag(SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS)) - return false; - - $resolveRecipients = new SyncResolveRecipients(); - $resolveRecipients->Decode(self::$decoder); - - if(!self::$decoder->getElementEndTag()) - return false; // SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS - - $resolveRecipients = self::$backend->ResolveRecipients($resolveRecipients); - - - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS); - - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_STATUS); - self::$encoder->content($resolveRecipients->status); - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_STATUS - - - foreach ($resolveRecipients->to as $i => $to) { - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_RESPONSE); - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_TO); - self::$encoder->content($to); - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_TO - - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_STATUS); - self::$encoder->content($resolveRecipients->status); - self::$encoder->endTag(); - - // do only if recipient is resolved - if ($resolveRecipients->status != SYNC_RESOLVERECIPSSTATUS_RESPONSE_UNRESOLVEDRECIP) { - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT); - self::$encoder->content(count($resolveRecipients->recipient)); - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT - - self::$encoder->startTag(SYNC_RESOLVERECIPIENTS_RECIPIENT); - $resolveRecipients->recipient[$i]->Encode(self::$encoder); - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_RECIPIENT - } - - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_RESPONSE - } - - self::$encoder->endTag(); // SYNC_RESOLVERECIPIENTS_RESOLVERECIPIENTS - return true; - } -} diff --git a/sources/lib/request/search.php b/sources/lib/request/search.php deleted file mode 100644 index a3f7dc1..0000000 --- a/sources/lib/request/search.php +++ /dev/null @@ -1,447 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Search extends RequestProcessor { - - /** - * Handles the Search command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $searchrange = '0'; - $cpo = new ContentParameters(); - - if(!self::$decoder->getElementStartTag(SYNC_SEARCH_SEARCH)) - return false; - - // TODO check: possible to search in other stores? - if(!self::$decoder->getElementStartTag(SYNC_SEARCH_STORE)) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_SEARCH_NAME)) - return false; - $searchname = strtoupper(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - - if(!self::$decoder->getElementStartTag(SYNC_SEARCH_QUERY)) - return false; - - // check if it is a content of an element (= GAL search) - // or a starttag (= mailbox or documentlibrary search) - $searchquery = self::$decoder->getElementContent(); - if($searchquery && !self::$decoder->getElementEndTag()) - return false; - - if ($searchquery === false) { - $cpo->SetSearchName($searchname); - if (self::$decoder->getElementStartTag(SYNC_SEARCH_AND)) { - if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) { - $searchfolderid = self::$decoder->getElementContent(); - $cpo->SetSearchFolderid($searchfolderid); - if(!self::$decoder->getElementEndTag()) // SYNC_FOLDERTYPE - return false; - } - - - if (self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { - $searchclass = self::$decoder->getElementContent(); - $cpo->SetSearchClass($searchclass); - if(!self::$decoder->getElementEndTag()) // SYNC_FOLDERTYPE - return false; - } - - if (self::$decoder->getElementStartTag(SYNC_FOLDERID)) { - $searchfolderid = self::$decoder->getElementContent(); - $cpo->SetSearchFolderid($searchfolderid); - if(!self::$decoder->getElementEndTag()) // SYNC_FOLDERTYPE - return false; - } - - if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) { - $searchfreetext = self::$decoder->getElementContent(); - $cpo->SetSearchFreeText($searchfreetext); - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_FREETEXT - return false; - } - - //TODO - review - if (self::$decoder->getElementStartTag(SYNC_SEARCH_GREATERTHAN)) { - if(self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) { - $datereceivedgreater = true; - if (($dam = self::$decoder->getElementContent()) !== false) { - $datereceivedgreater = true; - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - $cpo->SetSearchDateReceivedGreater($datereceivedgreater); - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { - $searchvalue = self::$decoder->getElementContent(); - $cpo->SetSearchValueGreater($searchvalue); - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_VALUE - return false; - } - - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_GREATERTHAN - return false; - } - - if (self::$decoder->getElementStartTag(SYNC_SEARCH_LESSTHAN)) { - if(self::$decoder->getElementStartTag(SYNC_POOMMAIL_DATERECEIVED)) { - $datereceivedless = true; - if (($dam = self::$decoder->getElementContent()) !== false) { - $datereceivedless = true; - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - $cpo->SetSearchDateReceivedLess($datereceivedless); - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { - $searchvalue = self::$decoder->getElementContent(); - $cpo->SetSearchValueLess($searchvalue); - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_VALUE - return false; - } - - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_LESSTHAN - return false; - } - - if (self::$decoder->getElementStartTag(SYNC_SEARCH_FREETEXT)) { - $searchfreetext = self::$decoder->getElementContent(); - $cpo->SetSearchFreeText($searchfreetext); - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_FREETEXT - return false; - } - - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_AND - return false; - } - elseif (self::$decoder->getElementStartTag(SYNC_SEARCH_EQUALTO)) { - // linkid can be an empty tag as well as have value - if(self::$decoder->getElementStartTag(SYNC_DOCUMENTLIBRARY_LINKID)) { - if (($linkId = self::$decoder->getElementContent()) !== false) { - $cpo->SetLinkId($linkId); - if(!self::$decoder->getElementEndTag()) { // SYNC_DOCUMENTLIBRARY_LINKID - return false; - } - } - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_VALUE)) { - $searchvalue = self::$decoder->getElementContent(); - $cpo->SetSearchValueLess($searchvalue); - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_VALUE - return false; - } - - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_EQUALTO - return false; - } - - if(!self::$decoder->getElementEndTag()) // SYNC_SEARCH_QUERY - return false; - - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_OPTIONS)) { - while(1) { - if(self::$decoder->getElementStartTag(SYNC_SEARCH_RANGE)) { - $searchrange = self::$decoder->getElementContent(); - $cpo->SetSearchRange($searchrange); - if(!self::$decoder->getElementEndTag()) - return false; - } - - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_REBUILDRESULTS)) { - $rebuildresults = true; - if (($dam = self::$decoder->getElementContent()) !== false) { - $rebuildresults = true; - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - $cpo->SetSearchRebuildResults($rebuildresults); - } - - if(self::$decoder->getElementStartTag(SYNC_SEARCH_DEEPTRAVERSAL)) { - $deeptraversal = true; - if (($dam = self::$decoder->getElementContent()) !== false) { - $deeptraversal = true; - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - $cpo->SetSearchDeepTraversal($deeptraversal); - } - - if(self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) { - $cpo->SetMimeSupport(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - //TODO body preferences - while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) { - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) { - $bptype = self::$decoder->getElementContent(); - $cpo->BodyPreference($bptype); - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) { - $cpo->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) { - $cpo->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) { - $cpo->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(!self::$decoder->getElementEndTag()) - return false; - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - } - if(!self::$decoder->getElementEndTag()) //store - return false; - - if(!self::$decoder->getElementEndTag()) //search - return false; - - // get SearchProvider - $searchprovider = ZPush::GetSearchProvider(); - $status = SYNC_SEARCHSTATUS_SUCCESS; - $rows = array(); - - // TODO support other searches - if ($searchprovider->SupportsType($searchname)) { - $storestatus = SYNC_SEARCHSTATUS_STORE_SUCCESS; - try { - if ($searchname == ISearchProvider::SEARCH_GAL) { - //get search results from the searchprovider - $rows = $searchprovider->GetGALSearchResults($searchquery, $searchrange); - } - elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) { - $rows = $searchprovider->GetMailboxSearchResults($cpo); - } - } - catch (StatusException $stex) { - $storestatus = $stex->getCode(); - } - } - else { - $rows = array('searchtotal' => 0); - $status = SYNC_SEARCHSTATUS_SERVERERROR; - ZLog::Write(LOGLEVEL_WARN, sprintf("Searchtype '%s' is not supported.", $searchname)); - self::$topCollector->AnnounceInformation(sprintf("Unsupported type '%s''", $searchname), true); - } - $searchprovider->Disconnect(); - - self::$topCollector->AnnounceInformation(sprintf("'%s' search found %d results", $searchname, $rows['searchtotal']), true); - - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_SEARCH_SEARCH); - - self::$encoder->startTag(SYNC_SEARCH_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - if ($status == SYNC_SEARCHSTATUS_SUCCESS) { - self::$encoder->startTag(SYNC_SEARCH_RESPONSE); - self::$encoder->startTag(SYNC_SEARCH_STORE); - - self::$encoder->startTag(SYNC_SEARCH_STATUS); - self::$encoder->content($storestatus); - self::$encoder->endTag(); - - if (isset($rows['range'])) { - $searchrange = $rows['range']; - unset($rows['range']); - } - if (isset($rows['searchtotal'])) { - $searchtotal = $rows['searchtotal']; - unset($rows['searchtotal']); - } - if ($searchname == ISearchProvider::SEARCH_GAL) { - if (is_array($rows) && !empty($rows)) { - foreach ($rows as $u) { - self::$encoder->startTag(SYNC_SEARCH_RESULT); - self::$encoder->startTag(SYNC_SEARCH_PROPERTIES); - - self::$encoder->startTag(SYNC_GAL_DISPLAYNAME); - self::$encoder->content((isset($u[SYNC_GAL_DISPLAYNAME]))?$u[SYNC_GAL_DISPLAYNAME]:"No name"); - self::$encoder->endTag(); - - if (isset($u[SYNC_GAL_PHONE])) { - self::$encoder->startTag(SYNC_GAL_PHONE); - self::$encoder->content($u[SYNC_GAL_PHONE]); - self::$encoder->endTag(); - } - - if (isset($u[SYNC_GAL_OFFICE])) { - self::$encoder->startTag(SYNC_GAL_OFFICE); - self::$encoder->content($u[SYNC_GAL_OFFICE]); - self::$encoder->endTag(); - } - - if (isset($u[SYNC_GAL_TITLE])) { - self::$encoder->startTag(SYNC_GAL_TITLE); - self::$encoder->content($u[SYNC_GAL_TITLE]); - self::$encoder->endTag(); - } - - if (isset($u[SYNC_GAL_COMPANY])) { - self::$encoder->startTag(SYNC_GAL_COMPANY); - self::$encoder->content($u[SYNC_GAL_COMPANY]); - self::$encoder->endTag(); - } - - if (isset($u[SYNC_GAL_ALIAS])) { - self::$encoder->startTag(SYNC_GAL_ALIAS); - self::$encoder->content($u[SYNC_GAL_ALIAS]); - self::$encoder->endTag(); - } - - // Always send the firstname, even empty. Nokia needs this to display the entry - self::$encoder->startTag(SYNC_GAL_FIRSTNAME); - self::$encoder->content((isset($u[SYNC_GAL_FIRSTNAME]))?$u[SYNC_GAL_FIRSTNAME]:""); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_GAL_LASTNAME); - self::$encoder->content((isset($u[SYNC_GAL_LASTNAME]))?$u[SYNC_GAL_LASTNAME]:"No name"); - self::$encoder->endTag(); - - if (isset($u[SYNC_GAL_HOMEPHONE])) { - self::$encoder->startTag(SYNC_GAL_HOMEPHONE); - self::$encoder->content($u[SYNC_GAL_HOMEPHONE]); - self::$encoder->endTag(); - } - - if (isset($u[SYNC_GAL_MOBILEPHONE])) { - self::$encoder->startTag(SYNC_GAL_MOBILEPHONE); - self::$encoder->content($u[SYNC_GAL_MOBILEPHONE]); - self::$encoder->endTag(); - } - - self::$encoder->startTag(SYNC_GAL_EMAILADDRESS); - self::$encoder->content((isset($u[SYNC_GAL_EMAILADDRESS]))?$u[SYNC_GAL_EMAILADDRESS]:""); - self::$encoder->endTag(); - - self::$encoder->endTag();//result - self::$encoder->endTag();//properties - } - } - } - elseif ($searchname == ISearchProvider::SEARCH_MAILBOX) { - if (is_array($rows) && !empty($rows)) { - foreach ($rows as $u) { - self::$encoder->startTag(SYNC_SEARCH_RESULT); - self::$encoder->startTag(SYNC_FOLDERTYPE); - self::$encoder->content($u['class']); - self::$encoder->endTag(); - self::$encoder->startTag(SYNC_SEARCH_LONGID); - self::$encoder->content($u['longid']); - self::$encoder->endTag(); - self::$encoder->startTag(SYNC_FOLDERID); - self::$encoder->content($u['folderid']); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_SEARCH_PROPERTIES); - $tmp = explode(":", $u['longid']); - $message = self::$backend->Fetch($u['folderid'], $tmp[1], $cpo); - $message->Encode(self::$encoder); - - self::$encoder->endTag();//result - self::$encoder->endTag();//properties - } - } - } - // it seems that android 4 requires range and searchtotal - // or it won't display the search results - if (isset($searchrange)) { - self::$encoder->startTag(SYNC_SEARCH_RANGE); - self::$encoder->content($searchrange); - self::$encoder->endTag(); - } - if (isset($searchtotal) && $searchtotal > 0) { - self::$encoder->startTag(SYNC_SEARCH_TOTAL); - self::$encoder->content($searchtotal); - self::$encoder->endTag(); - } - - self::$encoder->endTag();//store - self::$encoder->endTag();//response - } - self::$encoder->endTag();//search - - return true; - } -} diff --git a/sources/lib/request/sendmail.php b/sources/lib/request/sendmail.php deleted file mode 100644 index cda6edb..0000000 --- a/sources/lib/request/sendmail.php +++ /dev/null @@ -1,137 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SendMail extends RequestProcessor { - - /** - * Handles the SendMail, SmartReply and SmartForward command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - $status = SYNC_COMMONSTATUS_SUCCESS; - $sm = new SyncSendMail(); - - $reply = $forward = $parent = $sendmail = $smartreply = $smartforward = false; - if (Request::GetGETCollectionId()) - $parent = Request::GetGETCollectionId(); - if ($commandCode == ZPush::COMMAND_SMARTFORWARD) - $forward = Request::GetGETItemId(); - else if ($commandCode == ZPush::COMMAND_SMARTREPLY) - $reply = Request::GetGETItemId(); - - if (self::$decoder->IsWBXML()) { - $el = self::$decoder->getElement(); - - if($el[EN_TYPE] != EN_TYPE_STARTTAG) - return false; - - $commandTag = $el[EN_TAG]; - if($commandTag == SYNC_COMPOSEMAIL_SENDMAIL) - $sendmail = true; - else if($commandTag == SYNC_COMPOSEMAIL_SMARTREPLY) - $smartreply = true; - else if($commandTag == SYNC_COMPOSEMAIL_SMARTFORWARD) - $smartforward = true; - - if(!$sendmail && !$smartreply && !$smartforward) - return false; - - $sm->Decode(self::$decoder); - } - else { - $sm->mime = self::$decoder->GetPlainInputStream(); - // no wbxml output is provided, only a http OK - $sm->saveinsent = Request::GetGETSaveInSent(); - } - // Check if it is a reply or forward. Two cases are possible: - // 1. Either $smartreply or $smartforward are set after reading WBXML - // 2. Either $reply or $forward are set after geting the request parameters - if ($reply || $smartreply || $forward || $smartforward) { - // If the mobile sends an email in WBXML data the variables below - // should be set. If it is a RFC822 message, get the reply/forward message id - // from the request as they are always available there - if (!isset($sm->source)) $sm->source = new SyncSendMailSource(); - if (!isset($sm->source->itemid)) $sm->source->itemid = Request::GetGETItemId(); - if (!isset($sm->source->folderid)) $sm->source->folderid = Request::GetGETCollectionId(); - - // replyflag and forward flags are actually only for the correct icon. - // Even if they are a part of SyncSendMail object, they won't be streamed. - if ($smartreply || $reply) - $sm->replyflag = true; - else - $sm->forwardflag = true; - - if (!isset($sm->source->folderid)) - ZLog::Write(LOGLEVEL_ERROR, sprintf("No parent folder id while replying or forwarding message:'%s'", (($reply) ? $reply : $forward))); - } - - self::$topCollector->AnnounceInformation(sprintf("Sending email with %d bytes", strlen($sm->mime)), true); - - try { - $status = self::$backend->SendMail($sm); - } - catch (StatusException $se) { - $status = $se->getCode(); - $statusMessage = $se->getMessage(); - } - - if ($status != SYNC_COMMONSTATUS_SUCCESS) { - if (self::$decoder->IsWBXML()) { - self::$encoder->StartWBXML(); - self::$encoder->startTag($commandTag); - self::$encoder->startTag(SYNC_COMPOSEMAIL_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - self::$encoder->endTag(); - } - else - throw new HTTPReturnCodeException($statusMessage, HTTP_CODE_500, null, LOGLEVEL_WARN); - } - - return $status; - } -} diff --git a/sources/lib/request/settings.php b/sources/lib/request/settings.php deleted file mode 100644 index 7db79b9..0000000 --- a/sources/lib/request/settings.php +++ /dev/null @@ -1,231 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Settings extends RequestProcessor { - - /** - * Handles the Settings command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - if (!self::$decoder->getElementStartTag(SYNC_SETTINGS_SETTINGS)) - return false; - - //save the request parameters - $request = array(); - - // Loop through properties. Possible are: - // - Out of office - // - DevicePassword - // - DeviceInformation - // - UserInformation - // Each of them should only be once per request. Each property must be processed in order. - while (1) { - $propertyName = ""; - if (self::$decoder->getElementStartTag(SYNC_SETTINGS_OOF)) { - $propertyName = SYNC_SETTINGS_OOF; - } - if (self::$decoder->getElementStartTag(SYNC_SETTINGS_DEVICEPW)) { - $propertyName = SYNC_SETTINGS_DEVICEPW; - } - if (self::$decoder->getElementStartTag(SYNC_SETTINGS_DEVICEINFORMATION)) { - $propertyName = SYNC_SETTINGS_DEVICEINFORMATION; - } - if (self::$decoder->getElementStartTag(SYNC_SETTINGS_USERINFORMATION)) { - $propertyName = SYNC_SETTINGS_USERINFORMATION; - } - //TODO - check if it is necessary - //no property name available - break - if (!$propertyName) - break; - - //the property name is followed by either get or set - if (self::$decoder->getElementStartTag(SYNC_SETTINGS_GET)) { - //get is only available for OOF and user information - switch ($propertyName) { - case SYNC_SETTINGS_OOF: - $oofGet = new SyncOOF(); - $oofGet->Decode(self::$decoder); - if(!self::$decoder->getElementEndTag()) - return false; // SYNC_SETTINGS_GET - break; - - case SYNC_SETTINGS_USERINFORMATION: - $userInformation = new SyncUserInformation(); - break; - - default: - //TODO: a special status code needed? - ZLog::Write(LOGLEVEL_WARN, sprintf ("This property ('%s') is not allowed to use get in request", $propertyName)); - } - } - elseif (self::$decoder->getElementStartTag(SYNC_SETTINGS_SET)) { - //set is available for OOF, device password and device information - switch ($propertyName) { - case SYNC_SETTINGS_OOF: - $oofSet = new SyncOOF(); - $oofSet->Decode(self::$decoder); - //TODO check - do it after while(1) finished? - break; - - case SYNC_SETTINGS_DEVICEPW: - //TODO device password - $devicepassword = new SyncDevicePassword(); - $devicepassword->Decode(self::$decoder); - break; - - case SYNC_SETTINGS_DEVICEINFORMATION: - $deviceinformation = new SyncDeviceInformation(); - $deviceinformation->Decode(self::$decoder); - $deviceinformation->Status = SYNC_SETTINGSSTATUS_SUCCESS; - self::$deviceManager->SaveDeviceInformation($deviceinformation); - break; - - default: - //TODO: a special status code needed? - ZLog::Write(LOGLEVEL_WARN, sprintf ("This property ('%s') is not allowed to use set in request", $propertyName)); - } - - if(!self::$decoder->getElementEndTag()) - return false; // SYNC_SETTINGS_SET - } - else { - ZLog::Write(LOGLEVEL_WARN, sprintf("Neither get nor set found for property '%s'", $propertyName)); - return false; - } - - if(!self::$decoder->getElementEndTag()) - return false; // SYNC_SETTINGS_OOF or SYNC_SETTINGS_DEVICEPW or SYNC_SETTINGS_DEVICEINFORMATION or SYNC_SETTINGS_USERINFORMATION - - //break if it reached the endtag - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); //SYNC_SETTINGS_SETTINGS - break; - } - } - - $status = SYNC_SETTINGSSTATUS_SUCCESS; - - //TODO put it in try catch block - //TODO implement Settings in the backend - //TODO save device information in device manager - //TODO status handling -// $data = self::$backend->Settings($request); - - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_SETTINGS_SETTINGS); - - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - - //get oof settings - if (isset($oofGet)) { - $oofGet = self::$backend->Settings($oofGet); - self::$encoder->startTag(SYNC_SETTINGS_OOF); - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($oofGet->Status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - - self::$encoder->startTag(SYNC_SETTINGS_GET); - $oofGet->Encode(self::$encoder); - self::$encoder->endTag(); //SYNC_SETTINGS_GET - self::$encoder->endTag(); //SYNC_SETTINGS_OOF - } - - //get user information - //TODO none email address found - if (isset($userInformation)) { - self::$backend->Settings($userInformation); - self::$encoder->startTag(SYNC_SETTINGS_USERINFORMATION); - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($userInformation->Status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - - self::$encoder->startTag(SYNC_SETTINGS_GET); - $userInformation->Encode(self::$encoder); - self::$encoder->endTag(); //SYNC_SETTINGS_GET - self::$encoder->endTag(); //SYNC_SETTINGS_USERINFORMATION - } - - //set out of office - if (isset($oofSet)) { - $oofSet = self::$backend->Settings($oofSet); - self::$encoder->startTag(SYNC_SETTINGS_OOF); - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($oofSet->Status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - self::$encoder->endTag(); //SYNC_SETTINGS_OOF - } - - //set device passwort - if (isset($devicepassword)) { - self::$encoder->startTag(SYNC_SETTINGS_DEVICEPW); - self::$encoder->startTag(SYNC_SETTINGS_SET); - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($devicepassword->Status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - self::$encoder->endTag(); //SYNC_SETTINGS_SET - self::$encoder->endTag(); //SYNC_SETTINGS_DEVICEPW - } - - //set device information - if (isset($deviceinformation)) { - self::$encoder->startTag(SYNC_SETTINGS_DEVICEINFORMATION); - self::$encoder->startTag(SYNC_SETTINGS_STATUS); - self::$encoder->content($deviceinformation->Status); - self::$encoder->endTag(); //SYNC_SETTINGS_STATUS - self::$encoder->endTag(); //SYNC_SETTINGS_DEVICEINFORMATION - } - - - self::$encoder->endTag(); //SYNC_SETTINGS_SETTINGS - - return true; - } -} diff --git a/sources/lib/request/sync.php b/sources/lib/request/sync.php deleted file mode 100644 index 34990d3..0000000 --- a/sources/lib/request/sync.php +++ /dev/null @@ -1,1257 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Sync extends RequestProcessor { - // Ignored SMS identifier - const ZPUSHIGNORESMS = "ZPISMS"; - private $importer; - - /** - * Handles the Sync command - * Performs the synchronization of messages - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - // Contains all requested folders (containers) - $sc = new SyncCollections(); - $status = SYNC_STATUS_SUCCESS; - $wbxmlproblem = false; - $emptysync = false; - - - // check if the hierarchySync was fully completed - if (USE_PARTIAL_FOLDERSYNC) { - if (self::$deviceManager->GetFolderSyncComplete() === false) { - ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): Sync request aborted, as exporting of folders has not yet completed"); - self::$topCollector->AnnounceInformation("Aborted due incomplete folder sync", true); - $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED; - } - else - ZLog::Write(LOGLEVEL_INFO, "Request->HandleSync(): FolderSync marked as complete"); - } - - // Start Synchronize - if(self::$decoder->getElementStartTag(SYNC_SYNCHRONIZE)) { - - // AS 1.0 sends version information in WBXML - if(self::$decoder->getElementStartTag(SYNC_VERSION)) { - $sync_version = self::$decoder->getElementContent(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("WBXML sync version: '%s'", $sync_version)); - if(!self::$decoder->getElementEndTag()) - return false; - } - - // Synching specified folders - // Android still sends heartbeat sync even if all syncfolders are disabled. - // Check if Folders tag is empty () and only sync if there are - // some folders in the request. See ZP-172 - $startTag = self::$decoder->getElementStartTag(SYNC_FOLDERS); - if(isset($startTag[EN_FLAGS]) && $startTag[EN_FLAGS]) { - while(self::$decoder->getElementStartTag(SYNC_FOLDER)) { - $actiondata = array(); - $actiondata["requested"] = true; - $actiondata["clientids"] = array(); - $actiondata["modifyids"] = array(); - $actiondata["removeids"] = array(); - $actiondata["fetchids"] = array(); - $actiondata["statusids"] = array(); - - // read class, synckey and folderid without SyncParameters Object for now - $class = $synckey = $folderid = false; - - //for AS versions < 2.5 - if(self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { - $class = self::$decoder->getElementContent(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync folder: '%s'", $class)); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - // SyncKey - if(self::$decoder->getElementStartTag(SYNC_SYNCKEY)) { - $synckey = "0"; - if (($synckey = self::$decoder->getElementContent()) !== false) { - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - } - else - return false; - - // FolderId - if(self::$decoder->getElementStartTag(SYNC_FOLDERID)) { - $folderid = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - // compatibility mode AS 1.0 - get folderid which was sent during GetHierarchy() - if (! $folderid && $class) { - $folderid = self::$deviceManager->GetFolderIdFromCacheByClass($class); - } - - // folderid HAS TO BE known by now, so we retrieve the correct SyncParameters object for an update - try { - $spa = self::$deviceManager->GetStateManager()->GetSynchedFolderState($folderid); - - // TODO remove resync of folders for < Z-Push 2 beta4 users - // this forces a resync of all states previous to Z-Push 2 beta4 - if (! $spa instanceof SyncParameters) - throw new StateInvalidException("Saved state are not of type SyncParameters"); - - // new/resync requested - if ($synckey == "0") - $spa->RemoveSyncKey(); - else if ($synckey !== false) - $spa->SetSyncKey($synckey); - } - catch (StateInvalidException $stie) { - $spa = new SyncParameters(); - $status = SYNC_STATUS_INVALIDSYNCKEY; - self::$topCollector->AnnounceInformation("State invalid - Resync folder", true); - self::$deviceManager->ForceFolderResync($folderid); - } - - // update folderid.. this might be a new object - $spa->SetFolderId($folderid); - - if ($class !== false) - $spa->SetContentClass($class); - - // Get class for as versions >= 12.0 - if (! $spa->HasContentClass()) { - try { - $spa->SetContentClass(self::$deviceManager->GetFolderClassFromCacheByID($spa->GetFolderId())); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("GetFolderClassFromCacheByID from Device Manager: '%s' for id:'%s'", $spa->GetContentClass(), $spa->GetFolderId())); - } - catch (NoHierarchyCacheAvailableException $nhca) { - $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED; - self::$deviceManager->ForceFullResync(); - } - } - - // done basic SPA initialization/loading -> add to SyncCollection - $sc->AddCollection($spa); - $sc->AddParameter($spa, "requested", true); - - if ($spa->HasContentClass()) - self::$topCollector->AnnounceInformation(sprintf("%s request", $spa->GetContentClass()), true); - else - ZLog::Write(LOGLEVEL_WARN, "Not possible to determine class of request. Request did not contain class and apparently there is an issue with the HierarchyCache."); - - // SUPPORTED properties - if(($se = self::$decoder->getElementStartTag(SYNC_SUPPORTED)) !== false) { - // ZP-481: LG phones send an empty supported tag, so only read the contents if available here - // if is received, it's as no supported fields would have been sent at all. - // unsure if this is the correct approach, or if in this case some default list should be used - if ($se[EN_FLAGS] & EN_FLAGS_CONTENT) { - $supfields = array(); - while(1) { - $el = self::$decoder->getElement(); - - if($el[EN_TYPE] == EN_TYPE_ENDTAG) - break; - else - $supfields[] = $el[EN_TAG]; - } - self::$deviceManager->SetSupportedFields($spa->GetFolderId(), $supfields); - } - } - - // Deletes as moves can be an empty tag as well as have value - if(self::$decoder->getElementStartTag(SYNC_DELETESASMOVES)) { - $spa->SetDeletesAsMoves(true); - if (($dam = self::$decoder->getElementContent()) !== false) { - $spa->SetDeletesAsMoves((bool)$dam); - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - } - - // Get changes can be an empty tag as well as have value - // code block partly contributed by dw2412 - if(self::$decoder->getElementStartTag(SYNC_GETCHANGES)) { - $sc->AddParameter($spa, "getchanges", true); - if (($gc = self::$decoder->getElementContent()) !== false) { - $sc->AddParameter($spa, "getchanges", $gc); - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - } - - if(self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) { - $ws = self::$decoder->getElementContent(); - // normalize windowsize - see ZP-477 - if ($ws == 0 || $ws > 512) - $ws = 512; - - $spa->SetWindowSize($ws); - - // also announce the currently requested window size to the DeviceManager - self::$deviceManager->SetWindowSize($spa->GetFolderId(), $spa->GetWindowSize()); - - if(!self::$decoder->getElementEndTag()) - return false; - } - - // conversation mode requested - if(self::$decoder->getElementStartTag(SYNC_CONVERSATIONMODE)) { - $spa->SetConversationMode(true); - if(($conversationmode = self::$decoder->getElementContent()) !== false) { - $spa->SetConversationMode((bool)$conversationmode); - if(!self::$decoder->getElementEndTag()) - return false; - } - } - - // Do not truncate by default - $spa->SetTruncation(SYNC_TRUNCATION_ALL); - - // use default conflict handling if not specified by the mobile - $spa->SetConflict(SYNC_CONFLICT_DEFAULT); - - while(self::$decoder->getElementStartTag(SYNC_OPTIONS)) { - $firstOption = true; - while(1) { - // foldertype definition - if(self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { - $foldertype = self::$decoder->getElementContent(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): specified options block with foldertype '%s'", $foldertype)); - - // switch the foldertype for the next options - $spa->UseCPO($foldertype); - - // set to synchronize all changes. The mobile could overwrite this value - $spa->SetFilterType(SYNC_FILTERTYPE_ALL); - - if(!self::$decoder->getElementEndTag()) - return false; - } - // if no foldertype is defined, use default cpo - else if ($firstOption){ - $spa->UseCPO(); - // set to synchronize all changes. The mobile could overwrite this value - $spa->SetFilterType(SYNC_FILTERTYPE_ALL); - } - $firstOption = false; - - if(self::$decoder->getElementStartTag(SYNC_FILTERTYPE)) { - $spa->SetFilterType(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - if(self::$decoder->getElementStartTag(SYNC_TRUNCATION)) { - $spa->SetTruncation(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - if(self::$decoder->getElementStartTag(SYNC_RTFTRUNCATION)) { - $spa->SetRTFTruncation(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_MIMESUPPORT)) { - $spa->SetMimeSupport(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_MIMETRUNCATION)) { - $spa->SetMimeTruncation(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_CONFLICT)) { - $spa->SetConflict(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - while (self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_BODYPREFERENCE)) { - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TYPE)) { - $bptype = self::$decoder->getElementContent(); - $spa->BodyPreference($bptype); - if(!self::$decoder->getElementEndTag()) { - return false; - } - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_TRUNCATIONSIZE)) { - $spa->BodyPreference($bptype)->SetTruncationSize(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_ALLORNONE)) { - $spa->BodyPreference($bptype)->SetAllOrNone(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_AIRSYNCBASE_PREVIEW)) { - $spa->BodyPreference($bptype)->SetPreview(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) - return false; - } - - if(!self::$decoder->getElementEndTag()) - return false; - } - - $e = self::$decoder->peek(); - if($e[EN_TYPE] == EN_TYPE_ENDTAG) { - self::$decoder->getElementEndTag(); - break; - } - } - } - - // limit items to be synchronized to the mobiles if configured - if (defined('SYNC_FILTERTIME_MAX') && SYNC_FILTERTIME_MAX > SYNC_FILTERTYPE_ALL && - (!$spa->HasFilterType() || $spa->GetFilterType() == SYNC_FILTERTYPE_ALL || $spa->GetFilterType() > SYNC_FILTERTIME_MAX)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SYNC_FILTERTIME_MAX defined. Filter set to value: %s", SYNC_FILTERTIME_MAX)); - $spa->SetFilterType(SYNC_FILTERTIME_MAX); - } - - // Check if the hierarchycache is available. If not, trigger a HierarchySync - if (self::$deviceManager->IsHierarchySyncRequired()) { - $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED; - ZLog::Write(LOGLEVEL_DEBUG, "HierarchyCache is also not available. Triggering HierarchySync to device"); - } - - if(($el = self::$decoder->getElementStartTag(SYNC_PERFORM)) && ($el[EN_FLAGS] & EN_FLAGS_CONTENT)) { - // We can not proceed here as the content class is unknown - if ($status != SYNC_STATUS_SUCCESS) { - ZLog::Write(LOGLEVEL_WARN, "Ignoring all incoming actions as global status indicates problem."); - $wbxmlproblem = true; - break; - } - - $performaction = true; - - // unset the importer - $this->importer = false; - - $nchanges = 0; - while(1) { - // ADD, MODIFY, REMOVE or FETCH - $element = self::$decoder->getElement(); - - if($element[EN_TYPE] != EN_TYPE_STARTTAG) { - self::$decoder->ungetElement($element); - break; - } - - if ($status == SYNC_STATUS_SUCCESS) - $nchanges++; - - // Foldertype sent when synching SMS - if(self::$decoder->getElementStartTag(SYNC_FOLDERTYPE)) { - $foldertype = self::$decoder->getElementContent(); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): incoming data with foldertype '%s'", $foldertype)); - - if(!self::$decoder->getElementEndTag()) - return false; - } - else - $foldertype = false; - - $serverid = false; - if(self::$decoder->getElementStartTag(SYNC_SERVERENTRYID)) { - if (($serverid = self::$decoder->getElementContent()) !== false) { - if(!self::$decoder->getElementEndTag()) { // end serverid - return false; - } - } - } - - if(self::$decoder->getElementStartTag(SYNC_CLIENTENTRYID)) { - $clientid = self::$decoder->getElementContent(); - - if(!self::$decoder->getElementEndTag()) // end clientid - return false; - } - else - $clientid = false; - - // Get the SyncMessage if sent - if(self::$decoder->getElementStartTag(SYNC_DATA)) { - $message = ZPush::getSyncObjectFromFolderClass($spa->GetContentClass()); - $message->Decode(self::$decoder); - - // set Ghosted fields - $message->emptySupported(self::$deviceManager->GetSupportedFields($spa->GetFolderId())); - if(!self::$decoder->getElementEndTag()) // end applicationdata - return false; - } - else - $message = false; - - switch($element[EN_TAG]) { - case SYNC_FETCH: - array_push($actiondata["fetchids"], $serverid); - break; - default: - // get the importer - if ($this->importer == false) - $status = $this->getImporter($sc, $spa, $actiondata); - - if ($status == SYNC_STATUS_SUCCESS) - $this->importMessage($spa, $actiondata, $element[EN_TAG], $message, $clientid, $serverid, $foldertype, $nchanges); - else - ZLog::Write(LOGLEVEL_WARN, "Ignored incoming change, global status indicates problem."); - - break; - } - - if ($actiondata["fetchids"]) - self::$topCollector->AnnounceInformation(sprintf("Fetching %d", $nchanges)); - else - self::$topCollector->AnnounceInformation(sprintf("Incoming %d", $nchanges)); - - if(!self::$decoder->getElementEndTag()) // end add/change/delete/move - return false; - } - - if ($status == SYNC_STATUS_SUCCESS && $this->importer !== false) { - ZLog::Write(LOGLEVEL_INFO, sprintf("Sync->Handle(): Processed %d incoming changes", $nchanges)); - if (!$actiondata["fetchids"]) - self::$topCollector->AnnounceInformation(sprintf("%d incoming", $nchanges), true); - - try { - // Save the updated state, which is used for the exporter later - $sc->AddParameter($spa, "state", $this->importer->GetState()); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - } - - if(!self::$decoder->getElementEndTag()) // end PERFORM - return false; - } - - // save the failsave state - if (!empty($actiondata["statusids"])) { - unset($actiondata["failstate"]); - $actiondata["failedsyncstate"] = $sc->GetParameter($spa, "state"); - self::$deviceManager->GetStateManager()->SetSyncFailState($actiondata); - } - - // save actiondata - $sc->AddParameter($spa, "actiondata", $actiondata); - - if(!self::$decoder->getElementEndTag()) // end collection - return false; - - // AS14 does not send GetChanges anymore. We should do it if there were no incoming changes - if (!isset($performaction) && !$sc->GetParameter($spa, "getchanges") && $spa->HasSyncKey()) - $sc->AddParameter($spa, "getchanges", true); - } // END FOLDER - - if(!$wbxmlproblem && !self::$decoder->getElementEndTag()) // end collections - return false; - } // end FOLDERS - - if (self::$decoder->getElementStartTag(SYNC_HEARTBEATINTERVAL)) { - $hbinterval = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) // SYNC_HEARTBEATINTERVAL - return false; - } - - if (self::$decoder->getElementStartTag(SYNC_WAIT)) { - $wait = self::$decoder->getElementContent(); - if(!self::$decoder->getElementEndTag()) // SYNC_WAIT - return false; - - // internally the heartbeat interval and the wait time are the same - // heartbeat is in seconds, wait in minutes - $hbinterval = $wait * 60; - } - - if (self::$decoder->getElementStartTag(SYNC_WINDOWSIZE)) { - $sc->SetGlobalWindowSize(self::$decoder->getElementContent()); - if(!self::$decoder->getElementEndTag()) // SYNC_WINDOWSIZE - return false; - } - - if(self::$decoder->getElementStartTag(SYNC_PARTIAL)) - $partial = true; - else - $partial = false; - - if(!$wbxmlproblem && !self::$decoder->getElementEndTag()) // end sync - return false; - } - // we did not receive a SYNCHRONIZE block - assume empty sync - else { - $emptysync = true; - } - // END SYNCHRONIZE - - // check heartbeat/wait time - if (isset($hbinterval)) { - if ($hbinterval < 60 || $hbinterval > 3540) { - $status = SYNC_STATUS_INVALIDWAITORHBVALUE; - ZLog::Write(LOGLEVEL_WARN, sprintf("HandleSync(): Invalid heartbeat or wait value '%s'", $hbinterval)); - } - } - - // Partial & Empty Syncs need saved data to proceed with synchronization - if ($status == SYNC_STATUS_SUCCESS && ($emptysync === true || $partial === true) ) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Partial or Empty sync requested. Retrieving data of synchronized folders.")); - - // Load all collections - do not overwrite existing (received!), load states and check permissions - try { - $sc->LoadAllCollections(false, true, true); - } - catch (StateNotFoundException $snfex) { - $status = SYNC_STATUS_INVALIDSYNCKEY; - self::$topCollector->AnnounceInformation("StateNotFoundException", true); - } - catch (StatusException $stex) { - $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED; - self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true); - } - - // update a few values - foreach($sc as $folderid => $spa) { - // manually set getchanges parameter for this collection - $sc->AddParameter($spa, "getchanges", true); - - // set new global windowsize without marking the SPA as changed - if ($sc->GetGlobalWindowSize()) - $spa->SetWindowSize($sc->GetGlobalWindowSize(), false); - - // announce WindowSize to DeviceManager - self::$deviceManager->SetWindowSize($folderid, $spa->GetWindowSize()); - } - if (!$sc->HasCollections()) - $status = SYNC_STATUS_SYNCREQUESTINCOMPLETE; - } - - // HEARTBEAT & Empty sync - if ($status == SYNC_STATUS_SUCCESS && (isset($hbinterval) || $emptysync == true)) { - $interval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 30; - - if (isset($hbinterval)) - $sc->SetLifetime($hbinterval); - - // states are lazy loaded - we have to make sure that they are there! - $loadstatus = SYNC_STATUS_SUCCESS; - foreach($sc as $folderid => $spa) { - // some androids do heartbeat on the OUTBOX folder, with weird results - ZP-362 - // we do not load the state so we will never get relevant changes on the OUTBOX folder - if (self::$deviceManager->GetFolderTypeFromCacheById($folderid) == SYNC_FOLDER_TYPE_OUTBOX) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Heartbeat on Outbox folder not allowed")); - continue; - } - - $fad = array(); - // if loading the states fails, we do not enter heartbeat, but we keep $status on SYNC_STATUS_SUCCESS - // so when the changes are exported the correct folder gets an SYNC_STATUS_INVALIDSYNCKEY - if ($loadstatus == SYNC_STATUS_SUCCESS) - $loadstatus = $this->loadStates($sc, $spa, $fad); - } - - if ($loadstatus == SYNC_STATUS_SUCCESS) { - $foundchanges = false; - - try { - // always check for changes - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Entering Heartbeat mode")); - $foundchanges = $sc->CheckForChanges($sc->GetLifetime(), $interval); - } - catch (StatusException $stex) { - if ($stex->getCode() == SyncCollections::OBSOLETE_CONNECTION) { - $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID; - } - else { - $status = SYNC_STATUS_FOLDERHIERARCHYCHANGED; - self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true); - } - } - - // in case there are no changes and no other request has synchronized while we waited, we can reply with an empty response - if (!$foundchanges && $status == SYNC_STATUS_SUCCESS) { - // if there were changes to the SPA or CPOs we need to save this before we terminate - // only save if the state was not modified by some other request, if so, return state invalid status - foreach($sc as $folderid => $spa) { - if (self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) { - $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID; - } - else { - $sc->SaveCollection($spa); - } - } - - if ($status == SYNC_STATUS_SUCCESS) { - ZLog::Write(LOGLEVEL_DEBUG, "No changes found and no other process changed states. Replying with empty response and closing connection."); - self::$specialHeaders = array(); - self::$specialHeaders[] = "Connection: close"; - return true; - } - } - - if ($foundchanges) { - foreach ($sc->GetChangedFolderIds() as $folderid => $changecount) { - // check if there were other sync requests for a folder during the heartbeat - $spa = $sc->GetCollection($folderid); - if ($changecount > 0 && $sc->WaitedForChanges() && self::$deviceManager->CheckHearbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter())) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s' which was already synchronized. Heartbeat aborted!", $changecount, $folderid)); - $status = SYNC_COMMONSTATUS_SYNCSTATEVERSIONINVALID; - } - else - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): heartbeat: found %d changes in '%s'", $changecount, $folderid)); - } - } - } - } - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Start Output")); - - // Start the output - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_SYNCHRONIZE); - { - // global status - // SYNC_COMMONSTATUS_* start with values from 101 - if ($status != SYNC_COMMONSTATUS_SUCCESS && $status > 100) { - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - } - else { - self::$encoder->startTag(SYNC_FOLDERS); - { - foreach($sc as $folderid => $spa) { - // get actiondata - $actiondata = $sc->GetParameter($spa, "actiondata"); - - if ($status == SYNC_STATUS_SUCCESS && (!$spa->GetContentClass() || !$spa->GetFolderId())) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): no content class or folderid found for collection.")); - continue; - } - - if (! $sc->GetParameter($spa, "requested")) - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): partial sync for folder class '%s' with id '%s'", $spa->GetContentClass(), $spa->GetFolderId())); - - // initialize exporter to get changecount - $changecount = false; - if (isset($exporter)) - unset($exporter); - - // TODO we could check against $sc->GetChangedFolderIds() on heartbeat so we do not need to configure all exporter again - if($status == SYNC_STATUS_SUCCESS && ($sc->GetParameter($spa, "getchanges") || ! $spa->HasSyncKey())) { - - //make sure the states are loaded - $status = $this->loadStates($sc, $spa, $actiondata); - - if($status == SYNC_STATUS_SUCCESS) { - try { - // if this is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) - throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); - - // Use the state from the importer, as changes may have already happened - $exporter = self::$backend->GetExporter($spa->GetFolderId()); - - if ($exporter === false) - throw new StatusException(sprintf("HandleSync() could not get an exporter for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - try { - // Stream the messages directly to the PDA - $streamimporter = new ImportChangesStream(self::$encoder, ZPush::getSyncObjectFromFolderClass($spa->GetContentClass())); - - if ($exporter !== false) { - $exporter->Config($sc->GetParameter($spa, "state")); - $exporter->ConfigContentParameters($spa->GetCPO()); - $exporter->InitializeExporter($streamimporter); - - $changecount = $exporter->GetChangeCount(); - } - } - catch (StatusException $stex) { - if ($stex->getCode() === SYNC_FSSTATUS_CODEUNKNOWN && $spa->HasSyncKey()) - $status = SYNC_STATUS_INVALIDSYNCKEY; - else - $status = $stex->getCode(); - } - - if (! $spa->HasSyncKey()) { - self::$topCollector->AnnounceInformation(sprintf("Exporter registered. %d objects queued.", $changecount), true); - // update folder status as initialized - $spa->SetFolderSyncTotal($changecount); - $spa->SetFolderSyncRemaining($changecount); - if ($changecount > 0) { - self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INITIALIZED); - } - } - else if ($status != SYNC_STATUS_SUCCESS) - self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true); - - } - } - - if (isset($hbinterval) && $changecount == 0 && $status == SYNC_STATUS_SUCCESS) { - ZLog::Write(LOGLEVEL_DEBUG, "No changes found for heartbeat folder. Omitting empty output."); - continue; - } - - // Get a new sync key to output to the client if any changes have been send or will are available - if (!empty($actiondata["modifyids"]) || - !empty($actiondata["clientids"]) || - !empty($actiondata["removeids"]) || - $changecount > 0 || (! $spa->HasSyncKey() && $status == SYNC_STATUS_SUCCESS)) - $spa->SetNewSyncKey(self::$deviceManager->GetStateManager()->GetNewSyncKey($spa->GetSyncKey())); - - self::$encoder->startTag(SYNC_FOLDER); - - if($spa->HasContentClass()) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Folder type: %s", $spa->GetContentClass())); - // AS 12.0 devices require content class - if (Request::GetProtocolVersion() < 12.1) { - self::$encoder->startTag(SYNC_FOLDERTYPE); - self::$encoder->content($spa->GetContentClass()); - self::$encoder->endTag(); - } - } - - self::$encoder->startTag(SYNC_SYNCKEY); - if($status == SYNC_STATUS_SUCCESS && $spa->HasNewSyncKey()) - self::$encoder->content($spa->GetNewSyncKey()); - else - self::$encoder->content($spa->GetSyncKey()); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_FOLDERID); - self::$encoder->content($spa->GetFolderId()); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); - - // announce failing status to the process loop detection - if ($status !== SYNC_STATUS_SUCCESS) - self::$deviceManager->AnnounceProcessStatus($spa->GetFolderId(), $status); - - // Output IDs and status for incoming items & requests - if($status == SYNC_STATUS_SUCCESS && ( - !empty($actiondata["clientids"]) || - !empty($actiondata["modifyids"]) || - !empty($actiondata["removeids"]) || - !empty($actiondata["fetchids"]) )) { - - self::$encoder->startTag(SYNC_REPLIES); - // output result of all new incoming items - foreach($actiondata["clientids"] as $clientid => $serverid) { - self::$encoder->startTag(SYNC_ADD); - self::$encoder->startTag(SYNC_CLIENTENTRYID); - self::$encoder->content($clientid); - self::$encoder->endTag(); - if ($serverid) { - self::$encoder->startTag(SYNC_SERVERENTRYID); - self::$encoder->content($serverid); - self::$encoder->endTag(); - } - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content((isset($actiondata["statusids"][$clientid])?$actiondata["statusids"][$clientid]:SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR)); - self::$encoder->endTag(); - self::$encoder->endTag(); - } - - // loop through modify operations which were not a success, send status - foreach($actiondata["modifyids"] as $serverid) { - if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) { - self::$encoder->startTag(SYNC_MODIFY); - self::$encoder->startTag(SYNC_SERVERENTRYID); - self::$encoder->content($serverid); - self::$encoder->endTag(); - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content($actiondata["statusids"][$serverid]); - self::$encoder->endTag(); - self::$encoder->endTag(); - } - } - - // loop through remove operations which were not a success, send status - foreach($actiondata["removeids"] as $serverid) { - if (isset($actiondata["statusids"][$serverid]) && $actiondata["statusids"][$serverid] !== SYNC_STATUS_SUCCESS) { - self::$encoder->startTag(SYNC_REMOVE); - self::$encoder->startTag(SYNC_SERVERENTRYID); - self::$encoder->content($serverid); - self::$encoder->endTag(); - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content($actiondata["statusids"][$serverid]); - self::$encoder->endTag(); - self::$encoder->endTag(); - } - } - - if (!empty($actiondata["fetchids"])) - self::$topCollector->AnnounceInformation(sprintf("Fetching %d objects ", count($actiondata["fetchids"])), true); - - foreach($actiondata["fetchids"] as $id) { - $data = false; - try { - $fetchstatus = SYNC_STATUS_SUCCESS; - - // if this is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) - throw new StatusException(sprintf("HandleSync(): could not Setup() the backend to fetch in folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_OBJECTNOTFOUND); - - $data = self::$backend->Fetch($spa->GetFolderId(), $id, $spa->GetCPO()); - - // check if the message is broken - if (ZPush::GetDeviceManager(false) && ZPush::GetDeviceManager()->DoNotStreamMessage($id, $data)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): message not to be streamed as requested by DeviceManager, id = %s", $id)); - $fetchstatus = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR; - } - } - catch (StatusException $stex) { - $fetchstatus = $stex->getCode(); - } - - self::$encoder->startTag(SYNC_FETCH); - self::$encoder->startTag(SYNC_SERVERENTRYID); - self::$encoder->content($id); - self::$encoder->endTag(); - - self::$encoder->startTag(SYNC_STATUS); - self::$encoder->content($fetchstatus); - self::$encoder->endTag(); - - if($data !== false && $status == SYNC_STATUS_SUCCESS) { - self::$encoder->startTag(SYNC_DATA); - $data->Encode(self::$encoder); - self::$encoder->endTag(); - } - else - ZLog::Write(LOGLEVEL_WARN, sprintf("Unable to Fetch '%s'", $id)); - self::$encoder->endTag(); - - } - self::$encoder->endTag(); - } - - if($sc->GetParameter($spa, "getchanges") && $spa->HasFolderId() && $spa->HasContentClass() && $spa->HasSyncKey()) { - $windowSize = self::$deviceManager->GetWindowSize($spa->GetFolderId(), $spa->GetContentClass(), $spa->GetUuid(), $spa->GetUuidCounter(), $changecount); - - if($changecount > $windowSize) { - self::$encoder->startTag(SYNC_MOREAVAILABLE, false, true); - } - } - - // Stream outgoing changes - if($status == SYNC_STATUS_SUCCESS && $sc->GetParameter($spa, "getchanges") == true && $windowSize > 0) { - self::$topCollector->AnnounceInformation(sprintf("Streaming data of %d objects", (($changecount > $windowSize)?$windowSize:$changecount))); - - // Output message changes per folder - self::$encoder->startTag(SYNC_PERFORM); - - $n = 0; - while(1) { - try { - $progress = $exporter->Synchronize(); - if(!is_array($progress)) - break; - $n++; - } - catch (SyncObjectBrokenException $mbe) { - $brokenSO = $mbe->GetSyncObject(); - if (!$brokenSO) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but broken SyncObject not available. This should be fixed in the backend.")); - } - else { - if (!isset($brokenSO->id)) { - $brokenSO->id = "Unknown ID"; - ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): Catched SyncObjectBrokenException but no ID of object set. This should be fixed in the backend.")); - } - self::$deviceManager->AnnounceIgnoredMessage($spa->GetFolderId(), $brokenSO->id, $brokenSO); - } - } - // something really bad happened while exporting changes - catch (StatusException $stex) { - $status = $stex->getCode(); - // during export we found out that the states should be thrown away (ZP-623) - if ($status == SYNC_STATUS_INVALIDSYNCKEY) { - self::$deviceManager->ForceFolderResync($spa->GetFolderId()); - break; - } - } - - if($n >= $windowSize) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("HandleSync(): Exported maxItems of messages: %d / %d", $n, $changecount)); - break; - } - } - - // $progress is not an array when exporting the last message - // so we get the number to display from the streamimporter - if (isset($streamimporter)) { - $n = $streamimporter->GetImportedMessages(); - } - - self::$encoder->endTag(); - self::$topCollector->AnnounceInformation(sprintf("Outgoing %d objects%s", $n, ($n >= $windowSize)?" of ".$changecount:""), true); - - // update folder status, if there is something set - if ($spa->GetFolderSyncRemaining() && $changecount > 0) { - $spa->SetFolderSyncRemaining($changecount); - } - // changecount is initialized with 'false', so 0 means no changes! - if ($changecount === 0 || ($changecount !== false && $changecount <= $windowSize)) - self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_COMPLETED); - else - self::$deviceManager->SetFolderSyncStatus($folderid, DeviceManager::FLD_SYNC_INPROGRESS); - } - - self::$encoder->endTag(); - - // Save the sync state for the next time - if($spa->HasNewSyncKey()) { - self::$topCollector->AnnounceInformation("Saving state"); - - try { - if (isset($exporter) && $exporter) - $state = $exporter->GetState(); - - // nothing exported, but possibly imported - get the importer state - else if ($sc->GetParameter($spa, "state") !== null) - $state = $sc->GetParameter($spa, "state"); - - // if a new request without state information (hierarchy) save an empty state - else if (! $spa->HasSyncKey()) - $state = ""; - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - - if (isset($state) && $status == SYNC_STATUS_SUCCESS) - self::$deviceManager->GetStateManager()->SetSyncState($spa->GetNewSyncKey(), $state, $spa->GetFolderId()); - else - ZLog::Write(LOGLEVEL_ERROR, sprintf("HandleSync(): error saving '%s' - no state information available", $spa->GetNewSyncKey())); - } - - // save SyncParameters - if ($status == SYNC_STATUS_SUCCESS && empty($actiondata["fetchids"])) - $sc->SaveCollection($spa); - - // reset status for the next folder - $status = SYNC_STATUS_SUCCESS; - - - } // END foreach collection - } - self::$encoder->endTag(); //SYNC_FOLDERS - } - } - self::$encoder->endTag(); //SYNC_SYNCHRONIZE - - return true; - } - - /** - * Loads the states and writes them into the SyncCollection Object and the actiondata failstate - * - * @param SyncCollection $sc SyncCollection object - * @param SyncParameters $spa SyncParameters object - * @param array $actiondata Actiondata array - * @param boolean $loadFailsave (opt) default false - indicates if the failsave states should be loaded - * - * @access private - * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS - */ - private function loadStates($sc, $spa, &$actiondata, $loadFailsave = false) { - $status = SYNC_STATUS_SUCCESS; - - if ($sc->GetParameter($spa, "state") == null) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Sync->loadStates(): loading states for folder '%s'",$spa->GetFolderId())); - - try { - $sc->AddParameter($spa, "state", self::$deviceManager->GetStateManager()->GetSyncState($spa->GetSyncKey())); - - if ($loadFailsave) { - // if this request was made before, there will be a failstate available - $actiondata["failstate"] = self::$deviceManager->GetStateManager()->GetSyncFailState(); - } - - // if this is an additional folder the backend has to be setup correctly - if (!self::$backend->Setup(ZPush::GetAdditionalSyncFolderStore($spa->GetFolderId()))) - throw new StatusException(sprintf("HandleSync() could not Setup() the backend for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); - } - catch (StateNotFoundException $snfex) { - $status = SYNC_STATUS_INVALIDSYNCKEY; - self::$topCollector->AnnounceInformation("StateNotFoundException", true); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - self::$topCollector->AnnounceInformation(sprintf("StatusException code: %d", $status), true); - } - } - - return $status; - } - - /** - * Initializes the importer for the SyncParameters folder, loads necessary - * states (incl. failsave states) and initializes the conflict detection - * - * @param SyncCollection $sc SyncCollection object - * @param SyncParameters $spa SyncParameters object - * @param array $actiondata Actiondata array - * - * @access private - * @return status indicating if there were errors. If no errors, status is SYNC_STATUS_SUCCESS - */ - private function getImporter($sc, $spa, &$actiondata) { - ZLog::Write(LOGLEVEL_DEBUG, "Sync->getImporter(): initialize importer"); - $status = SYNC_STATUS_SUCCESS; - - // load the states with failsave data - $status = $this->loadStates($sc, $spa, $actiondata, true); - - try { - // Configure importer with last state - $this->importer = self::$backend->GetImporter($spa->GetFolderId()); - - // if something goes wrong, ask the mobile to resync the hierarchy - if ($this->importer === false) - throw new StatusException(sprintf("Sync->getImporter(): no importer for folder id '%s'", $spa->GetFolderId()), SYNC_STATUS_FOLDERHIERARCHYCHANGED); - - // if there is a valid state obtained after importing changes in a previous loop, we use that state - if (isset($actiondata["failstate"]) && isset($actiondata["failstate"]["failedsyncstate"])) { - $this->importer->Config($actiondata["failstate"]["failedsyncstate"], $spa->GetConflict()); - } - else - $this->importer->Config($sc->GetParameter($spa, "state"), $spa->GetConflict()); - - // the CPO is also needed by the importer to check if imported changes are inside the sync window - see ZP-258 - $this->importer->ConfigContentParameters($spa->GetCPO()); - } - catch (StatusException $stex) { - $status = $stex->getCode(); - } - - $this->importer->LoadConflicts($spa->GetCPO(), $sc->GetParameter($spa, "state")); - - return $status; - } - - /** - * Imports a message - * - * @param SyncParameters $spa SyncParameters object - * @param array $actiondata Actiondata array - * @param integer $todo WBXML flag indicating how message should be imported. - * Valid values: SYNC_ADD, SYNC_MODIFY, SYNC_REMOVE - * @param SyncObject $message SyncObject message to be imported - * @param string $clientid Client message identifier - * @param string $serverid Server message identifier - * @param string $foldertype On sms sync, this says "SMS", else false - * @param integer $messageCount Counter of already imported messages - * - * @access private - * @throws StatusException in case the importer is not available - * @return - Message related status are returned in the actiondata. - */ - private function importMessage($spa, &$actiondata, $todo, $message, $clientid, $serverid, $foldertype, $messageCount) { - // the importer needs to be available! - if ($this->importer == false) - throw StatusException("Sync->importMessage(): importer not available", SYNC_STATUS_SERVERERROR); - - // mark this state as used, e.g. for HeartBeat - self::$deviceManager->SetHeartbeatStateIntegrity($spa->GetFolderId(), $spa->GetUuid(), $spa->GetUuidCounter()); - - // Detect incoming loop - // messages which were created/removed before will not have the same action executed again - // if a message is edited we perform this action "again", as the message could have been changed on the mobile in the meantime - $ignoreMessage = false; - if ($actiondata["failstate"]) { - // message was ADDED before, do NOT add it again - if ($todo == SYNC_ADD && isset($actiondata["failstate"]["clientids"][$clientid])) { - $ignoreMessage = true; - - // make sure no messages are sent back - self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0); - - $actiondata["clientids"][$clientid] = $actiondata["failstate"]["clientids"][$clientid]; - $actiondata["statusids"][$clientid] = $actiondata["failstate"]["statusids"][$clientid]; - - ZLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Incoming new message '%s' was created on the server before. Replying with known new server id: %s", $clientid, $actiondata["clientids"][$clientid])); - } - - // message was REMOVED before, do NOT attemp to remove it again - if ($todo == SYNC_REMOVE && isset($actiondata["failstate"]["removeids"][$serverid])) { - $ignoreMessage = true; - - // make sure no messages are sent back - self::$deviceManager->SetWindowSize($spa->GetFolderId(), 0); - - $actiondata["removeids"][$serverid] = $actiondata["failstate"]["removeids"][$serverid]; - $actiondata["statusids"][$serverid] = $actiondata["failstate"]["statusids"][$serverid]; - - ZLog::Write(LOGLEVEL_WARN, sprintf("Mobile loop detected! Message '%s' was deleted by the mobile before. Replying with known status: %s", $clientid, $actiondata["statusids"][$serverid])); - } - } - - if (!$ignoreMessage) { - switch($todo) { - case SYNC_MODIFY: - self::$topCollector->AnnounceInformation(sprintf("Saving modified message %d", $messageCount)); - try { - $actiondata["modifyids"][] = $serverid; - - // ignore sms messages - if ($foldertype == "SMS" || stripos($serverid, self::ZPUSHIGNORESMS) !== false) { - ZLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message."); - // TODO we should update the SMS - $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS; - } - // check incoming message without logging WARN messages about errors - else if (!($message instanceof SyncObject) || !$message->Check(true)) { - $actiondata["statusids"][$serverid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR; - } - else { - if(isset($message->read)) { - // Currently, 'read' is only sent by the PDA when it is ONLY setting the read flag. - $this->importer->ImportMessageReadFlag($serverid, $message->read); - } - elseif (!isset($message->flag)) { - $this->importer->ImportMessageChange($serverid, $message); - } - - // email todoflags - some devices send todos flags together with read flags, - // so they have to be handled separately - if (isset($message->flag)){ - $this->importer->ImportMessageChange($serverid, $message); - } - - $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS; - } - } - catch (StatusException $stex) { - $actiondata["statusids"][$serverid] = $stex->getCode(); - } - - break; - case SYNC_ADD: - self::$topCollector->AnnounceInformation(sprintf("Creating new message from mobile %d", $messageCount)); - try { - // ignore sms messages - if ($foldertype == "SMS") { - ZLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message."); - // TODO we should create the SMS - // return a fake serverid which we can identify later - $actiondata["clientids"][$clientid] = self::ZPUSHIGNORESMS . $clientid; - $actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS; - } - // check incoming message without logging WARN messages about errors - else if (!($message instanceof SyncObject) || !$message->Check(true)) { - $actiondata["clientids"][$clientid] = false; - $actiondata["statusids"][$clientid] = SYNC_STATUS_CLIENTSERVERCONVERSATIONERROR; - } - else { - $actiondata["clientids"][$clientid] = false; - $actiondata["clientids"][$clientid] = $this->importer->ImportMessageChange(false, $message); - $actiondata["statusids"][$clientid] = SYNC_STATUS_SUCCESS; - } - } - catch (StatusException $stex) { - $actiondata["statusids"][$clientid] = $stex->getCode(); - } - break; - case SYNC_REMOVE: - self::$topCollector->AnnounceInformation(sprintf("Deleting message removed on mobile %d", $messageCount)); - try { - $actiondata["removeids"][] = $serverid; - // ignore sms messages - if ($foldertype == "SMS" || stripos($serverid, self::ZPUSHIGNORESMS) !== false) { - ZLog::Write(LOGLEVEL_DEBUG, "SMS sync are not supported. Ignoring message."); - // TODO we should delete the SMS - $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS; - } - else { - // if message deletions are to be moved, move them - if($spa->GetDeletesAsMoves()) { - $folderid = self::$backend->GetWasteBasket(); - - if($folderid) { - $this->importer->ImportMessageMove($serverid, $folderid); - $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS; - break; - } - else - ZLog::Write(LOGLEVEL_WARN, "Message should be moved to WasteBasket, but the Backend did not return a destination ID. Message is hard deleted now!"); - } - - $this->importer->ImportMessageDeletion($serverid); - $actiondata["statusids"][$serverid] = SYNC_STATUS_SUCCESS; - } - } - catch (StatusException $stex) { - $actiondata["statusids"][$serverid] = $stex->getCode(); - } - break; - } - ZLog::Write(LOGLEVEL_DEBUG, "Sync->importMessage(): message imported"); - } - } -} diff --git a/sources/lib/request/validatecert.php b/sources/lib/request/validatecert.php deleted file mode 100755 index 4215837..0000000 --- a/sources/lib/request/validatecert.php +++ /dev/null @@ -1,89 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ValidateCert extends RequestProcessor { - - /** - * Handles the ValidateCert command - * - * @param int $commandCode - * - * @access public - * @return boolean - */ - public function Handle($commandCode) { - // Parse input - if(!self::$decoder->getElementStartTag(SYNC_VALIDATECERT_VALIDATECERT)) - return false; - - $validateCert = new SyncValidateCert(); - $validateCert->Decode(self::$decoder); - $cert_der = base64_decode($validateCert->certificates[0]); - $cert_pem = "-----BEGIN CERTIFICATE-----\n".chunk_split(base64_encode($cert_der), 64, "\n")."-----END CERTIFICATE-----\n"; - - $checkpurpose = (defined('CAINFO') && CAINFO) ? openssl_x509_checkpurpose($cert_pem, X509_PURPOSE_SMIME_SIGN, array(CAINFO)) : openssl_x509_checkpurpose($cert_pem, X509_PURPOSE_SMIME_SIGN); - if ($checkpurpose === true) - $status = SYNC_VALIDATECERTSTATUS_SUCCESS; - else - $status = SYNC_VALIDATECERTSTATUS_CANTVALIDATESIG; - - if(!self::$decoder->getElementEndTag()) - return false; // SYNC_VALIDATECERT_VALIDATECERT - - self::$encoder->startWBXML(); - self::$encoder->startTag(SYNC_VALIDATECERT_VALIDATECERT); - - self::$encoder->startTag(SYNC_VALIDATECERT_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); // SYNC_VALIDATECERT_STATUS - - self::$encoder->startTag(SYNC_VALIDATECERT_CERTIFICATE); - self::$encoder->startTag(SYNC_VALIDATECERT_STATUS); - self::$encoder->content($status); - self::$encoder->endTag(); // SYNC_VALIDATECERT_STATUS - self::$encoder->endTag(); // SYNC_VALIDATECERT_CERTIFICATE - - self::$encoder->endTag(); // SYNC_VALIDATECERT_VALIDATECERT - return true; - } -} diff --git a/sources/lib/syncobjects/syncappointment.php b/sources/lib/syncobjects/syncappointment.php deleted file mode 100644 index 755c1a5..0000000 --- a/sources/lib/syncobjects/syncappointment.php +++ /dev/null @@ -1,221 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncAppointment extends SyncObject { - public $timezone; - public $dtstamp; - public $starttime; - public $subject; - public $uid; - public $organizername; - public $organizeremail; - public $location; - public $endtime; - public $recurrence; - public $sensitivity; - public $busystatus; - public $alldayevent; - public $reminder; - public $rtf; - public $meetingstatus; - public $attendees; - public $body; - public $bodytruncated; - public $exceptions; - public $deleted; - public $exceptionstarttime; - public $categories; - - // AS 12.0 props - public $asbody; - public $nativebodytype; - - // AS 14.0 props - public $disallownewtimeprop; - public $responsetype; - public $responserequested; - - - function SyncAppointment() { - $mapping = array( - SYNC_POOMCAL_TIMEZONE => array ( self::STREAMER_VAR => "timezone"), - - SYNC_POOMCAL_DTSTAMP => array ( self::STREAMER_VAR => "dtstamp", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMCAL_STARTTIME => array ( self::STREAMER_VAR => "starttime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_CMPLOWER => SYNC_POOMCAL_ENDTIME ) ), - - - SYNC_POOMCAL_SUBJECT => array ( self::STREAMER_VAR => "subject", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY)), - - SYNC_POOMCAL_UID => array ( self::STREAMER_VAR => "uid"), - SYNC_POOMCAL_ORGANIZERNAME => array ( self::STREAMER_VAR => "organizername"), // verified below - SYNC_POOMCAL_ORGANIZEREMAIL => array ( self::STREAMER_VAR => "organizeremail"), // verified below - SYNC_POOMCAL_LOCATION => array ( self::STREAMER_VAR => "location"), - SYNC_POOMCAL_ENDTIME => array ( self::STREAMER_VAR => "endtime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE, - self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMCAL_STARTTIME ) ), - - SYNC_POOMCAL_RECURRENCE => array ( self::STREAMER_VAR => "recurrence", - self::STREAMER_TYPE => "SyncRecurrence"), - - // Sensitivity values - // 0 = Normal - // 1 = Personal - // 2 = Private - // 3 = Confident - SYNC_POOMCAL_SENSITIVITY => array ( self::STREAMER_VAR => "sensitivity", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3) )), - - // Busystatus values - // 0 = Free - // 1 = Tentative - // 2 = Busy - // 3 = Out of office - // 4 = Working Elsewhere - SYNC_POOMCAL_BUSYSTATUS => array ( self::STREAMER_VAR => "busystatus", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETTWO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,4) )), - - SYNC_POOMCAL_ALLDAYEVENT => array ( self::STREAMER_VAR => "alldayevent", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMCAL_REMINDER => array ( self::STREAMER_VAR => "reminder", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1)), - - SYNC_POOMCAL_RTF => array ( self::STREAMER_VAR => "rtf"), - - // Meetingstatus values - // 0 = is not a meeting - // 1 = is a meeting - // 3 = Meeting received - // 5 = Meeting is canceled - // 7 = Meeting is canceled and received - // 9 = as 1 - // 11 = as 3 - // 13 = as 5 - // 15 = as 7 - SYNC_POOMCAL_MEETINGSTATUS => array ( self::STREAMER_VAR => "meetingstatus", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,3,5,7,9,11,13,15) )), - - SYNC_POOMCAL_ATTENDEES => array ( self::STREAMER_VAR => "attendees", - self::STREAMER_TYPE => "SyncAttendee", - self::STREAMER_ARRAY => SYNC_POOMCAL_ATTENDEE), - - SYNC_POOMCAL_BODY => array ( self::STREAMER_VAR => "body"), - SYNC_POOMCAL_BODYTRUNCATED => array ( self::STREAMER_VAR => "bodytruncated"), - SYNC_POOMCAL_EXCEPTIONS => array ( self::STREAMER_VAR => "exceptions", - self::STREAMER_TYPE => "SyncAppointmentException", - self::STREAMER_ARRAY => SYNC_POOMCAL_EXCEPTION), - - SYNC_POOMCAL_CATEGORIES => array ( self::STREAMER_VAR => "categories", - self::STREAMER_ARRAY => SYNC_POOMCAL_CATEGORY), - ); - - if (Request::GetProtocolVersion() >= 12.0) { - $mapping[SYNC_AIRSYNCBASE_BODY] = array ( self::STREAMER_VAR => "asbody", - self::STREAMER_TYPE => "SyncBaseBody"); - - $mapping[SYNC_AIRSYNCBASE_NATIVEBODYTYPE] = array ( self::STREAMER_VAR => "nativebodytype"); - - //unset these properties because airsyncbase body and attachments will be used instead - unset($mapping[SYNC_POOMCAL_BODY], $mapping[SYNC_POOMCAL_BODYTRUNCATED]); - } - - if(Request::GetProtocolVersion() >= 14.0) { - $mapping[SYNC_POOMCAL_DISALLOWNEWTIMEPROPOSAL] = array ( self::STREAMER_VAR => "disallownewtimeprop"); - $mapping[SYNC_POOMCAL_RESPONSEREQUESTED] = array ( self::STREAMER_VAR => "responserequested"); - $mapping[SYNC_POOMCAL_RESPONSETYPE] = array ( self::STREAMER_VAR => "responsetype"); - } - - parent::SyncObject($mapping); - } - - /** - * Method checks if the object has the minimum of required parameters - * and fullfills semantic dependencies - * - * This overloads the general check() with special checks to be executed - * Checks if SYNC_POOMCAL_ORGANIZERNAME and SYNC_POOMCAL_ORGANIZEREMAIL are correctly set - * - * @param boolean $logAsDebug (opt) default is false, so messages are logged in WARN log level - * - * @access public - * @return boolean - */ - public function Check($logAsDebug = false) { - $ret = parent::Check($logAsDebug); - - // semantic checks general "turn off switch" - if (defined("DO_SEMANTIC_CHECKS") && DO_SEMANTIC_CHECKS === false) - return $ret; - - if (!$ret) - return false; - - if ($this->meetingstatus > 0) { - if (!isset($this->organizername) || !isset($this->organizeremail)) { - ZLog::Write(LOGLEVEL_WARN, "SyncAppointment->Check(): Parameter 'organizername' and 'organizeremail' should be set for a meeting request"); - } - } - - // do not sync a recurrent appointment without a timezone - if (isset($this->recurrence) && !isset($this->timezone)) { - ZLog::Write(LOGLEVEL_ERROR, "SyncAppointment->Check(): timezone for a recurring appointment is not set."); - return false; - } - - return true; - } -} diff --git a/sources/lib/syncobjects/syncappointmentexception.php b/sources/lib/syncobjects/syncappointmentexception.php deleted file mode 100644 index 35c0a91..0000000 --- a/sources/lib/syncobjects/syncappointmentexception.php +++ /dev/null @@ -1,76 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncAppointmentException extends SyncAppointment { - public $deleted; - public $exceptionstarttime; - - function SyncAppointmentException() { - parent::SyncAppointment(); - - $this->mapping += array( - SYNC_POOMCAL_DELETED => array ( self::STREAMER_VAR => "deleted", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMCAL_EXCEPTIONSTARTTIME => array ( self::STREAMER_VAR => "exceptionstarttime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE)), - ); - - // some parameters are not required in an exception, others are not allowed to be set in SyncAppointmentExceptions - $this->mapping[SYNC_POOMCAL_TIMEZONE][self::STREAMER_CHECKS] = array(); - $this->mapping[SYNC_POOMCAL_DTSTAMP][self::STREAMER_CHECKS] = array(); - $this->mapping[SYNC_POOMCAL_STARTTIME][self::STREAMER_CHECKS] = array(self::STREAMER_CHECK_CMPLOWER => SYNC_POOMCAL_ENDTIME); - $this->mapping[SYNC_POOMCAL_SUBJECT][self::STREAMER_CHECKS] = array(); - $this->mapping[SYNC_POOMCAL_ENDTIME][self::STREAMER_CHECKS] = array(self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMCAL_STARTTIME); - $this->mapping[SYNC_POOMCAL_BUSYSTATUS][self::STREAMER_CHECKS] = array(self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,4) ); - $this->mapping[SYNC_POOMCAL_REMINDER][self::STREAMER_CHECKS] = array(self::STREAMER_CHECK_CMPHIGHER => -1); - $this->mapping[SYNC_POOMCAL_EXCEPTIONS][self::STREAMER_CHECKS] = array(self::STREAMER_CHECK_NOTALLOWED => true); - - } -} diff --git a/sources/lib/syncobjects/syncattachment.php b/sources/lib/syncobjects/syncattachment.php deleted file mode 100644 index 0c25e00..0000000 --- a/sources/lib/syncobjects/syncattachment.php +++ /dev/null @@ -1,76 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncAttachment extends SyncObject { - public $attmethod; - public $attsize; - public $displayname; - public $attname; - public $attoid; - public $attremoved; - - function SyncAttachment() { - $mapping = array( - SYNC_POOMMAIL_ATTMETHOD => array ( self::STREAMER_VAR => "attmethod"), - SYNC_POOMMAIL_ATTSIZE => array ( self::STREAMER_VAR => "attsize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_CMPHIGHER => -1 )), - - SYNC_POOMMAIL_DISPLAYNAME => array ( self::STREAMER_VAR => "displayname", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY)), - - SYNC_POOMMAIL_ATTNAME => array ( self::STREAMER_VAR => "attname", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY)), - - SYNC_POOMMAIL_ATTOID => array ( self::STREAMER_VAR => "attoid"), - SYNC_POOMMAIL_ATTREMOVED => array ( self::STREAMER_VAR => "attremoved"), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncattendee.php b/sources/lib/syncobjects/syncattendee.php deleted file mode 100644 index f8b4c84..0000000 --- a/sources/lib/syncobjects/syncattendee.php +++ /dev/null @@ -1,69 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncAttendee extends SyncObject { - public $email; - public $name; - - function SyncAttendee() { - $mapping = array( - SYNC_POOMCAL_EMAIL => array ( self::STREAMER_VAR => "email", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY)), - - SYNC_POOMCAL_NAME => array ( self::STREAMER_VAR => "name", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY) ) - ); - - if (Request::GetProtocolVersion() >= 12.0) { - $mapping[SYNC_POOMCAL_ATTENDEESTATUS] = array ( self::STREAMER_VAR => "attendeestatus"); - $mapping[SYNC_POOMCAL_ATTENDEETYPE] = array ( self::STREAMER_VAR => "attendeetype"); - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncbaseattachment.php b/sources/lib/syncobjects/syncbaseattachment.php deleted file mode 100644 index 9c0e128..0000000 --- a/sources/lib/syncobjects/syncbaseattachment.php +++ /dev/null @@ -1,70 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncBaseAttachment extends SyncObject { - public $displayname; - public $filereference; - public $method; - public $estimatedDataSize; - public $contentid; - public $contentlocation; - public $isinline; - - function SyncBaseAttachment() { - $mapping = array( - SYNC_AIRSYNCBASE_DISPLAYNAME => array (self::STREAMER_VAR => "displayname"), - SYNC_AIRSYNCBASE_FILEREFERENCE => array (self::STREAMER_VAR => "filereference"), - SYNC_AIRSYNCBASE_METHOD => array (self::STREAMER_VAR => "method"), - SYNC_AIRSYNCBASE_ESTIMATEDDATASIZE => array (self::STREAMER_VAR => "estimatedDataSize"), - SYNC_AIRSYNCBASE_CONTENTID => array (self::STREAMER_VAR => "contentid"), - SYNC_AIRSYNCBASE_CONTENTLOCATION => array (self::STREAMER_VAR => "contentlocation"), - SYNC_AIRSYNCBASE_ISINLINE => array (self::STREAMER_VAR => "isinline"), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncbasebody.php b/sources/lib/syncobjects/syncbasebody.php deleted file mode 100644 index d19166f..0000000 --- a/sources/lib/syncobjects/syncbasebody.php +++ /dev/null @@ -1,67 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncBaseBody extends SyncObject { - public $type; //Possible types are plain text, html, rtf and mime - public $estimatedDataSize; - public $truncated; - public $data; - public $preview; - - function SyncBaseBody() { - $mapping = array( - SYNC_AIRSYNCBASE_TYPE => array (self::STREAMER_VAR => "type"), - SYNC_AIRSYNCBASE_ESTIMATEDDATASIZE => array (self::STREAMER_VAR => "estimatedDataSize"), - SYNC_AIRSYNCBASE_TRUNCATED => array (self::STREAMER_VAR => "truncated"), - SYNC_AIRSYNCBASE_DATA => array (self::STREAMER_VAR => "data"), - ); - if(Request::GetProtocolVersion() >= 14.0) { - $mapping[SYNC_AIRSYNCBASE_PREVIEW] = array (self::STREAMER_VAR => "preview"); - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/synccontact.php b/sources/lib/syncobjects/synccontact.php deleted file mode 100644 index c20596b..0000000 --- a/sources/lib/syncobjects/synccontact.php +++ /dev/null @@ -1,205 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncContact extends SyncObject { - public $anniversary; - public $assistantname; - public $assistnamephonenumber; - public $birthday; - public $body; - public $bodysize; - public $bodytruncated; - public $business2phonenumber; - public $businesscity; - public $businesscountry; - public $businesspostalcode; - public $businessstate; - public $businessstreet; - public $businessfaxnumber; - public $businessphonenumber; - public $carphonenumber; - public $children; - public $companyname; - public $department; - public $email1address; - public $email2address; - public $email3address; - public $fileas; - public $firstname; - public $home2phonenumber; - public $homecity; - public $homecountry; - public $homepostalcode; - public $homestate; - public $homestreet; - public $homefaxnumber; - public $homephonenumber; - public $jobtitle; - public $lastname; - public $middlename; - public $mobilephonenumber; - public $officelocation; - public $othercity; - public $othercountry; - public $otherpostalcode; - public $otherstate; - public $otherstreet; - public $pagernumber; - public $radiophonenumber; - public $spouse; - public $suffix; - public $title; - public $webpage; - public $yomicompanyname; - public $yomifirstname; - public $yomilastname; - public $rtf; - public $picture; - public $categories; - - // AS 2.5 props - public $customerid; - public $governmentid; - public $imaddress; - public $imaddress2; - public $imaddress3; - public $managername; - public $companymainphone; - public $accountname; - public $nickname; - public $mms; - - // AS 12 props - public $asbody; - - public function __construct() { - $mapping = array ( - SYNC_POOMCONTACTS_ANNIVERSARY => array ( self::STREAMER_VAR => "anniversary", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES ), - - SYNC_POOMCONTACTS_ASSISTANTNAME => array ( self::STREAMER_VAR => "assistantname"), - SYNC_POOMCONTACTS_ASSISTNAMEPHONENUMBER => array ( self::STREAMER_VAR => "assistnamephonenumber"), - SYNC_POOMCONTACTS_BIRTHDAY => array ( self::STREAMER_VAR => "birthday", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES ), - - SYNC_POOMCONTACTS_BUSINESS2PHONENUMBER => array ( self::STREAMER_VAR => "business2phonenumber"), - SYNC_POOMCONTACTS_BUSINESSCITY => array ( self::STREAMER_VAR => "businesscity"), - SYNC_POOMCONTACTS_BUSINESSCOUNTRY => array ( self::STREAMER_VAR => "businesscountry"), - SYNC_POOMCONTACTS_BUSINESSPOSTALCODE => array ( self::STREAMER_VAR => "businesspostalcode"), - SYNC_POOMCONTACTS_BUSINESSSTATE => array ( self::STREAMER_VAR => "businessstate"), - SYNC_POOMCONTACTS_BUSINESSSTREET => array ( self::STREAMER_VAR => "businessstreet"), - SYNC_POOMCONTACTS_BUSINESSFAXNUMBER => array ( self::STREAMER_VAR => "businessfaxnumber"), - SYNC_POOMCONTACTS_BUSINESSPHONENUMBER => array ( self::STREAMER_VAR => "businessphonenumber"), - SYNC_POOMCONTACTS_CARPHONENUMBER => array ( self::STREAMER_VAR => "carphonenumber"), - SYNC_POOMCONTACTS_CHILDREN => array ( self::STREAMER_VAR => "children", - self::STREAMER_ARRAY => SYNC_POOMCONTACTS_CHILD ), - - SYNC_POOMCONTACTS_COMPANYNAME => array ( self::STREAMER_VAR => "companyname"), - SYNC_POOMCONTACTS_DEPARTMENT => array ( self::STREAMER_VAR => "department"), - SYNC_POOMCONTACTS_EMAIL1ADDRESS => array ( self::STREAMER_VAR => "email1address"), - SYNC_POOMCONTACTS_EMAIL2ADDRESS => array ( self::STREAMER_VAR => "email2address"), - SYNC_POOMCONTACTS_EMAIL3ADDRESS => array ( self::STREAMER_VAR => "email3address"), - SYNC_POOMCONTACTS_FILEAS => array ( self::STREAMER_VAR => "fileas"), - SYNC_POOMCONTACTS_FIRSTNAME => array ( self::STREAMER_VAR => "firstname"), - SYNC_POOMCONTACTS_HOME2PHONENUMBER => array ( self::STREAMER_VAR => "home2phonenumber"), - SYNC_POOMCONTACTS_HOMECITY => array ( self::STREAMER_VAR => "homecity"), - SYNC_POOMCONTACTS_HOMECOUNTRY => array ( self::STREAMER_VAR => "homecountry"), - SYNC_POOMCONTACTS_HOMEPOSTALCODE => array ( self::STREAMER_VAR => "homepostalcode"), - SYNC_POOMCONTACTS_HOMESTATE => array ( self::STREAMER_VAR => "homestate"), - SYNC_POOMCONTACTS_HOMESTREET => array ( self::STREAMER_VAR => "homestreet"), - SYNC_POOMCONTACTS_HOMEFAXNUMBER => array ( self::STREAMER_VAR => "homefaxnumber"), - SYNC_POOMCONTACTS_HOMEPHONENUMBER => array ( self::STREAMER_VAR => "homephonenumber"), - SYNC_POOMCONTACTS_JOBTITLE => array ( self::STREAMER_VAR => "jobtitle"), - SYNC_POOMCONTACTS_LASTNAME => array ( self::STREAMER_VAR => "lastname"), - SYNC_POOMCONTACTS_MIDDLENAME => array ( self::STREAMER_VAR => "middlename"), - SYNC_POOMCONTACTS_MOBILEPHONENUMBER => array ( self::STREAMER_VAR => "mobilephonenumber"), - SYNC_POOMCONTACTS_OFFICELOCATION => array ( self::STREAMER_VAR => "officelocation"), - SYNC_POOMCONTACTS_OTHERCITY => array ( self::STREAMER_VAR => "othercity"), - SYNC_POOMCONTACTS_OTHERCOUNTRY => array ( self::STREAMER_VAR => "othercountry"), - SYNC_POOMCONTACTS_OTHERPOSTALCODE => array ( self::STREAMER_VAR => "otherpostalcode"), - SYNC_POOMCONTACTS_OTHERSTATE => array ( self::STREAMER_VAR => "otherstate"), - SYNC_POOMCONTACTS_OTHERSTREET => array ( self::STREAMER_VAR => "otherstreet"), - SYNC_POOMCONTACTS_PAGERNUMBER => array ( self::STREAMER_VAR => "pagernumber"), - SYNC_POOMCONTACTS_RADIOPHONENUMBER => array ( self::STREAMER_VAR => "radiophonenumber"), - SYNC_POOMCONTACTS_SPOUSE => array ( self::STREAMER_VAR => "spouse"), - SYNC_POOMCONTACTS_SUFFIX => array ( self::STREAMER_VAR => "suffix"), - SYNC_POOMCONTACTS_TITLE => array ( self::STREAMER_VAR => "title"), - SYNC_POOMCONTACTS_WEBPAGE => array ( self::STREAMER_VAR => "webpage"), - SYNC_POOMCONTACTS_YOMICOMPANYNAME => array ( self::STREAMER_VAR => "yomicompanyname"), - SYNC_POOMCONTACTS_YOMIFIRSTNAME => array ( self::STREAMER_VAR => "yomifirstname"), - SYNC_POOMCONTACTS_YOMILASTNAME => array ( self::STREAMER_VAR => "yomilastname"), - SYNC_POOMCONTACTS_RTF => array ( self::STREAMER_VAR => "rtf"), - SYNC_POOMCONTACTS_PICTURE => array ( self::STREAMER_VAR => "picture", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_LENGTHMAX => SYNC_CONTACTS_MAXPICTURESIZE )), - - SYNC_POOMCONTACTS_CATEGORIES => array ( self::STREAMER_VAR => "categories", - self::STREAMER_ARRAY => SYNC_POOMCONTACTS_CATEGORY ), - - SYNC_POOMCONTACTS2_CUSTOMERID => array ( self::STREAMER_VAR => "customerid"), - SYNC_POOMCONTACTS2_GOVERNMENTID => array ( self::STREAMER_VAR => "governmentid"), - SYNC_POOMCONTACTS2_IMADDRESS => array ( self::STREAMER_VAR => "imaddress"), - SYNC_POOMCONTACTS2_IMADDRESS2 => array ( self::STREAMER_VAR => "imaddress2"), - SYNC_POOMCONTACTS2_IMADDRESS3 => array ( self::STREAMER_VAR => "imaddress3"), - SYNC_POOMCONTACTS2_MANAGERNAME => array ( self::STREAMER_VAR => "managername"), - SYNC_POOMCONTACTS2_COMPANYMAINPHONE => array ( self::STREAMER_VAR => "companymainphone"), - SYNC_POOMCONTACTS2_ACCOUNTNAME => array ( self::STREAMER_VAR => "accountname"), - SYNC_POOMCONTACTS2_NICKNAME => array ( self::STREAMER_VAR => "nickname"), - SYNC_POOMCONTACTS2_MMS => array ( self::STREAMER_VAR => "mms"), - ); - - if (Request::GetProtocolVersion() >= 12.0) { - $mapping[SYNC_AIRSYNCBASE_BODY] = array ( self::STREAMER_VAR => "asbody", - self::STREAMER_TYPE => "SyncBaseBody"); - } else { - $mapping[SYNC_POOMCONTACTS_BODY] = array ( self::STREAMER_VAR => "body"); - $mapping[SYNC_POOMCONTACTS_BODYSIZE] = array ( self::STREAMER_VAR => "bodysize"); - $mapping[SYNC_POOMCONTACTS_BODYTRUNCATED] = array ( self::STREAMER_VAR => "bodytruncated"); - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncdeviceinformation.php b/sources/lib/syncobjects/syncdeviceinformation.php deleted file mode 100644 index 02308e5..0000000 --- a/sources/lib/syncobjects/syncdeviceinformation.php +++ /dev/null @@ -1,84 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncDeviceInformation extends SyncObject { - public $model; - public $imei; - public $friendlyname; - public $os; - public $oslanguage; - public $phonenumber; - public $useragent; //12.1 &14.0 - public $mobileoperator; //14.0 - public $enableoutboundsms; //14.0 - public $Status; - - public function SyncDeviceInformation() { - $mapping = array ( - SYNC_SETTINGS_MODEL => array ( self::STREAMER_VAR => "model"), - SYNC_SETTINGS_IMEI => array ( self::STREAMER_VAR => "imei"), - SYNC_SETTINGS_FRIENDLYNAME => array ( self::STREAMER_VAR => "friendlyname"), - SYNC_SETTINGS_OS => array ( self::STREAMER_VAR => "os"), - SYNC_SETTINGS_OSLANGUAGE => array ( self::STREAMER_VAR => "oslanguage"), - SYNC_SETTINGS_PHONENUMBER => array ( self::STREAMER_VAR => "phonenumber"), - - SYNC_SETTINGS_PROP_STATUS => array ( self::STREAMER_VAR => "Status", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE) - ); - - if (Request::GetProtocolVersion() >= 12.1) { - $mapping[SYNC_SETTINGS_USERAGENT] = array ( self::STREAMER_VAR => "useragent"); - } - - if (Request::GetProtocolVersion() >= 14.0) { - $mapping[SYNC_SETTINGS_MOBILEOPERATOR] = array ( self::STREAMER_VAR => "mobileoperator"); - $mapping[SYNC_SETTINGS_ENABLEOUTBOUNDSMS] = array ( self::STREAMER_VAR => "enableoutboundsms"); - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncdevicepassword.php b/sources/lib/syncobjects/syncdevicepassword.php deleted file mode 100644 index 21b257a..0000000 --- a/sources/lib/syncobjects/syncdevicepassword.php +++ /dev/null @@ -1,62 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncDevicePassword extends SyncObject { - public $password; - public $Status; - - public function SyncDevicePassword() { - $mapping = array ( - SYNC_SETTINGS_DEVICEPW => array ( self::STREAMER_VAR => "password"), - - SYNC_SETTINGS_PROP_STATUS => array ( self::STREAMER_VAR => "Status", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE) - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncfolder.php b/sources/lib/syncobjects/syncfolder.php deleted file mode 100644 index a3e23c4..0000000 --- a/sources/lib/syncobjects/syncfolder.php +++ /dev/null @@ -1,78 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncFolder extends SyncObject { - public $serverid; - public $parentid; - public $displayname; - public $type; - public $Store; - - function SyncFolder() { - $mapping = array ( - SYNC_FOLDERHIERARCHY_SERVERENTRYID => array ( self::STREAMER_VAR => "serverid", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => false)), - - SYNC_FOLDERHIERARCHY_PARENTID => array ( self::STREAMER_VAR => "parentid", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO)), - - SYNC_FOLDERHIERARCHY_DISPLAYNAME => array ( self::STREAMER_VAR => "displayname", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => "Unknown")), - - SYNC_FOLDERHIERARCHY_TYPE => array ( self::STREAMER_VAR => "type", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => 18, - self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 20 )), - - SYNC_FOLDERHIERARCHY_IGNORE_STORE => array ( self::STREAMER_VAR => "Store", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncitemoperationsattachment.php b/sources/lib/syncobjects/syncitemoperationsattachment.php deleted file mode 100644 index 6a520ad..0000000 --- a/sources/lib/syncobjects/syncitemoperationsattachment.php +++ /dev/null @@ -1,61 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncItemOperationsAttachment extends SyncObject { - public $contenttype; - public $data; - - function SyncItemOperationsAttachment() { - $mapping = array( - SYNC_AIRSYNCBASE_CONTENTTYPE => array ( self::STREAMER_VAR => "contenttype"), - SYNC_ITEMOPERATIONS_DATA => array ( self::STREAMER_VAR => "data", - self::STREAMER_TYPE => self::STREAMER_TYPE_STREAM, - self::STREAMER_PROP => self::STREAMER_TYPE_MULTIPART), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncmail.php b/sources/lib/syncobjects/syncmail.php deleted file mode 100644 index ef70b75..0000000 --- a/sources/lib/syncobjects/syncmail.php +++ /dev/null @@ -1,199 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncMail extends SyncObject { - public $to; - public $cc; - public $from; - public $subject; - public $threadtopic; - public $datereceived; - public $displayto; - public $importance; - public $read; - public $attachments; - public $mimetruncated; - public $mimedata; - public $mimesize; - public $bodytruncated; - public $bodysize; - public $body; - public $messageclass; - public $meetingrequest; - public $reply_to; - - // AS 2.5 prop - public $internetcpid; - - // AS 12.0 props - public $asbody; - public $asattachments; - public $flag; - public $contentclass; - public $nativebodytype; - - // AS 14.0 props - public $umcallerid; - public $umusernotes; - public $conversationid; - public $conversationindex; - public $lastverbexecuted; //possible values unknown, reply to sender, reply to all, forward - public $lastverbexectime; - public $receivedasbcc; - public $sender; - public $categories; - - function SyncMail() { - $mapping = array ( - SYNC_POOMMAIL_TO => array ( self::STREAMER_VAR => "to", - self::STREAMER_TYPE => self::STREAMER_TYPE_COMMA_SEPARATED, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_LENGTHMAX => 32768, - self::STREAMER_CHECK_EMAIL => "" )), - - SYNC_POOMMAIL_CC => array ( self::STREAMER_VAR => "cc", - self::STREAMER_TYPE => self::STREAMER_TYPE_COMMA_SEPARATED, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_LENGTHMAX => 32768, - self::STREAMER_CHECK_EMAIL => "" )), - - SYNC_POOMMAIL_FROM => array ( self::STREAMER_VAR => "from", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_LENGTHMAX => 32768, - self::STREAMER_CHECK_EMAIL => "" )), - - SYNC_POOMMAIL_SUBJECT => array ( self::STREAMER_VAR => "subject"), - SYNC_POOMMAIL_THREADTOPIC => array ( self::STREAMER_VAR => "threadtopic"), - SYNC_POOMMAIL_DATERECEIVED => array ( self::STREAMER_VAR => "datereceived", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMMAIL_DISPLAYTO => array ( self::STREAMER_VAR => "displayto"), - - // Importance values - // 0 = Low - // 1 = Normal - // 2 = High - // even the default value 1 is optional, the native android client 2.2 interprets a non-existing value as 0 (low) - SYNC_POOMMAIL_IMPORTANCE => array ( self::STREAMER_VAR => "importance", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2) )), - - SYNC_POOMMAIL_READ => array ( self::STREAMER_VAR => "read", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_POOMMAIL_ATTACHMENTS => array ( self::STREAMER_VAR => "attachments", - self::STREAMER_TYPE => "SyncAttachment", - self::STREAMER_ARRAY => SYNC_POOMMAIL_ATTACHMENT), - - SYNC_POOMMAIL_MIMETRUNCATED => array ( self::STREAMER_VAR => "mimetruncated", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMMAIL_MIMEDATA => array ( self::STREAMER_VAR => "mimedata"), //TODO mimedata should be of a type stream - - SYNC_POOMMAIL_MIMESIZE => array ( self::STREAMER_VAR => "mimesize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1)), - - SYNC_POOMMAIL_BODYTRUNCATED => array ( self::STREAMER_VAR => "bodytruncated", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMMAIL_BODYSIZE => array ( self::STREAMER_VAR => "bodysize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1)), - - SYNC_POOMMAIL_BODY => array ( self::STREAMER_VAR => "body"), - SYNC_POOMMAIL_MESSAGECLASS => array ( self::STREAMER_VAR => "messageclass"), - SYNC_POOMMAIL_MEETINGREQUEST => array ( self::STREAMER_VAR => "meetingrequest", - self::STREAMER_TYPE => "SyncMeetingRequest"), - - SYNC_POOMMAIL_REPLY_TO => array ( self::STREAMER_VAR => "reply_to", - self::STREAMER_TYPE => self::STREAMER_TYPE_SEMICOLON_SEPARATED, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_EMAIL => "" )), - - ); - - if (Request::GetProtocolVersion() >= 2.5) { - $mapping[SYNC_POOMMAIL_INTERNETCPID] = array ( self::STREAMER_VAR => "internetcpid"); - } - - if (Request::GetProtocolVersion() >= 12.0) { - $mapping[SYNC_AIRSYNCBASE_BODY] = array ( self::STREAMER_VAR => "asbody", - self::STREAMER_TYPE => "SyncBaseBody"); - - $mapping[SYNC_AIRSYNCBASE_ATTACHMENTS] = array ( self::STREAMER_VAR => "asattachments", - self::STREAMER_TYPE => "SyncBaseAttachment", - self::STREAMER_ARRAY => SYNC_AIRSYNCBASE_ATTACHMENT); - - $mapping[SYNC_POOMMAIL_CONTENTCLASS] = array ( self::STREAMER_VAR => "contentclass", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(DEFAULT_EMAIL_CONTENTCLASS, DEFAULT_CALENDAR_CONTENTCLASS) )); - - $mapping[SYNC_POOMMAIL_FLAG] = array ( self::STREAMER_VAR => "flag", - self::STREAMER_TYPE => "SyncMailFlags", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY); - - $mapping[SYNC_AIRSYNCBASE_NATIVEBODYTYPE] = array ( self::STREAMER_VAR => "nativebodytype"); - - //unset these properties because airsyncbase body and attachments will be used instead - unset($mapping[SYNC_POOMMAIL_BODY], $mapping[SYNC_POOMMAIL_BODYTRUNCATED], $mapping[SYNC_POOMMAIL_ATTACHMENTS]); - } - - if (Request::GetProtocolVersion() >= 14.0) { - $mapping[SYNC_POOMMAIL2_UMCALLERID] = array ( self::STREAMER_VAR => "umcallerid"); - $mapping[SYNC_POOMMAIL2_UMUSERNOTES] = array ( self::STREAMER_VAR => "umusernotes"); - $mapping[SYNC_POOMMAIL2_CONVERSATIONID] = array ( self::STREAMER_VAR => "conversationid"); - $mapping[SYNC_POOMMAIL2_CONVERSATIONINDEX] = array ( self::STREAMER_VAR => "conversationindex"); - $mapping[SYNC_POOMMAIL2_LASTVERBEXECUTED] = array ( self::STREAMER_VAR => "lastverbexecuted"); - - $mapping[SYNC_POOMMAIL2_LASTVERBEXECUTIONTIME] = array ( self::STREAMER_VAR => "lastverbexectime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES); - - $mapping[SYNC_POOMMAIL2_RECEIVEDASBCC] = array ( self::STREAMER_VAR => "receivedasbcc"); - $mapping[SYNC_POOMMAIL2_SENDER] = array ( self::STREAMER_VAR => "sender"); - $mapping[SYNC_POOMMAIL_CATEGORIES] = array ( self::STREAMER_VAR => "categories", - self::STREAMER_ARRAY => SYNC_POOMMAIL_CATEGORY); - //TODO bodypart, accountid, rightsmanagementlicense - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncmailflags.php b/sources/lib/syncobjects/syncmailflags.php deleted file mode 100644 index 6811470..0000000 --- a/sources/lib/syncobjects/syncmailflags.php +++ /dev/null @@ -1,98 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncMailFlags extends SyncObject { - public $subject; - public $flagstatus; - public $flagtype; //Possible types are clear, complete, active - public $datecompleted; - public $completetime; - public $startdate; - public $duedate; - public $utcstartdate; - public $utcduedate; - public $reminderset; - public $remindertime; - public $ordinaldate; - public $subordinaldate; - - - function SyncMailFlags() { - $mapping = array( - SYNC_POOMTASKS_SUBJECT => array ( self::STREAMER_VAR => "subject"), - SYNC_POOMMAIL_FLAGSTATUS => array ( self::STREAMER_VAR => "flagstatus"), - SYNC_POOMMAIL_FLAGTYPE => array ( self::STREAMER_VAR => "flagtype"), - SYNC_POOMTASKS_DATECOMPLETED => array ( self::STREAMER_VAR => "datecompleted", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMMAIL_COMPLETETIME => array ( self::STREAMER_VAR => "completetime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_STARTDATE => array ( self::STREAMER_VAR => "startdate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_DUEDATE => array ( self::STREAMER_VAR => "duedate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_UTCSTARTDATE => array ( self::STREAMER_VAR => "utcstartdate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_UTCDUEDATE => array ( self::STREAMER_VAR => "utcduedate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_REMINDERSET => array ( self::STREAMER_VAR => "reminderset"), - SYNC_POOMTASKS_REMINDERTIME => array ( self::STREAMER_VAR => "remindertime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_ORDINALDATE => array ( self::STREAMER_VAR => "ordinaldate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_SUBORDINALDATE => array ( self::STREAMER_VAR => "subordinaldate"), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncmeetingrequest.php b/sources/lib/syncobjects/syncmeetingrequest.php deleted file mode 100644 index f7c5aa4..0000000 --- a/sources/lib/syncobjects/syncmeetingrequest.php +++ /dev/null @@ -1,139 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncMeetingRequest extends SyncObject { - public $alldayevent; - public $starttime; - public $dtstamp; - public $endtime; - public $instancetype; - public $location; - public $organizer; - public $recurrenceid; - public $reminder; - public $responserequested; - public $recurrences; - public $sensitivity; - public $busystatus; - public $timezone; - public $globalobjid; - public $disallownewtimeproposal; - - function SyncMeetingRequest() { - $mapping = array ( - SYNC_POOMMAIL_ALLDAYEVENT => array ( self::STREAMER_VAR => "alldayevent", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)), - - SYNC_POOMMAIL_STARTTIME => array ( self::STREAMER_VAR => "starttime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_CMPLOWER => SYNC_POOMMAIL_ENDTIME ) ), - - SYNC_POOMMAIL_DTSTAMP => array ( self::STREAMER_VAR => "dtstamp", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO) ), - - SYNC_POOMMAIL_ENDTIME => array ( self::STREAMER_VAR => "endtime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE, - self::STREAMER_CHECK_CMPHIGHER => SYNC_POOMMAIL_STARTTIME ) ), - // Instancetype values - // 0 = single appointment - // 1 = master recurring appointment - // 2 = single instance of recurring appointment - // 3 = exception of recurring appointment - SYNC_POOMMAIL_INSTANCETYPE => array ( self::STREAMER_VAR => "instancetype", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3) )), - - SYNC_POOMMAIL_LOCATION => array ( self::STREAMER_VAR => "location"), - SYNC_POOMMAIL_ORGANIZER => array ( self::STREAMER_VAR => "organizer", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETEMPTY ) ), - - SYNC_POOMMAIL_RECURRENCEID => array ( self::STREAMER_VAR => "recurrenceid", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMMAIL_REMINDER => array ( self::STREAMER_VAR => "reminder", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1)), - - SYNC_POOMMAIL_RESPONSEREQUESTED => array ( self::STREAMER_VAR => "responserequested"), - SYNC_POOMMAIL_RECURRENCES => array ( self::STREAMER_VAR => "recurrences", - self::STREAMER_TYPE => "SyncMeetingRequestRecurrence", - self::STREAMER_ARRAY => SYNC_POOMMAIL_RECURRENCE), - // Sensitivity values - // 0 = Normal - // 1 = Personal - // 2 = Private - // 3 = Confident - SYNC_POOMMAIL_SENSITIVITY => array ( self::STREAMER_VAR => "sensitivity", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3) )), - - // Busystatus values - // 0 = Free - // 1 = Tentative - // 2 = Busy - // 3 = Out of office - // 4 = Working Elsewhere - SYNC_POOMMAIL_BUSYSTATUS => array ( self::STREAMER_VAR => "busystatus", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETTWO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,4) )), - - SYNC_POOMMAIL_TIMEZONE => array ( self::STREAMER_VAR => "timezone", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => base64_encode(pack("la64vvvvvvvv"."la64vvvvvvvv"."l",0,"",0,0,0,0,0,0,0,0,0,"",0,0,0,0,0,0,0,0,0)) )), - - SYNC_POOMMAIL_GLOBALOBJID => array ( self::STREAMER_VAR => "globalobjid"), - - SYNC_POOMMAIL_DISALLOWNEWTIMEPROPOSAL => array ( self::STREAMER_VAR => "disallownewtimeproposal", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncmeetingrequestrecurrence.php b/sources/lib/syncobjects/syncmeetingrequestrecurrence.php deleted file mode 100644 index 6b86237..0000000 --- a/sources/lib/syncobjects/syncmeetingrequestrecurrence.php +++ /dev/null @@ -1,119 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncMeetingRequestRecurrence extends SyncObject { - public $type; - public $until; - public $occurrences; - public $interval; - public $dayofweek; - public $dayofmonth; - public $weekofmonth; - public $monthofyear; - - function SyncMeetingRequestRecurrence() { - $mapping = array ( - // Recurrence type - // 0 = Recurs daily - // 1 = Recurs weekly - // 2 = Recurs monthly - // 3 = Recurs monthly on the nth day - // 5 = Recurs yearly - // 6 = Recurs yearly on the nth day - SYNC_POOMMAIL_TYPE => array ( self::STREAMER_VAR => "type", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,5,6) )), - - SYNC_POOMMAIL_UNTIL => array ( self::STREAMER_VAR => "until", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE), - - SYNC_POOMMAIL_OCCURRENCES => array ( self::STREAMER_VAR => "occurrences", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - SYNC_POOMMAIL_INTERVAL => array ( self::STREAMER_VAR => "interval", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - // DayOfWeek values - // 1 = Sunday - // 2 = Monday - // 4 = Tuesday - // 8 = Wednesday - // 16 = Thursday - // 32 = Friday - // 62 = Weekdays // not in spec: daily weekday recurrence - // 64 = Saturday - // 127 = The last day of the month. Value valid only in monthly or yearly recurrences. - // As this is a bitmask, actually all values 0 > x < 128 are allowed - SYNC_POOMMAIL_DAYOFWEEK => array ( self::STREAMER_VAR => "dayofweek", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 128 )), - - // DayOfMonth values - // 1-31 representing the day - SYNC_POOMMAIL_DAYOFMONTH => array ( self::STREAMER_VAR => "dayofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 32 )), - - // WeekOfMonth - // 1-4 = Y st/nd/rd/th week of month - // 5 = last week of month - SYNC_POOMMAIL_WEEKOFMONTH => array ( self::STREAMER_VAR => "weekofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5) )), - - // MonthOfYear - // 1-12 representing the month - SYNC_POOMMAIL_MONTHOFYEAR => array ( self::STREAMER_VAR => "monthofyear", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5,6,7,8,9,10,11,12) )), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncnote.php b/sources/lib/syncobjects/syncnote.php deleted file mode 100644 index 64bdfc2..0000000 --- a/sources/lib/syncobjects/syncnote.php +++ /dev/null @@ -1,74 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncNote extends SyncObject { - public $asbody; - public $categories; - public $lastmodified; - public $messageclass; - public $subject; - - function SyncNote() { - $mapping = array( - SYNC_AIRSYNCBASE_BODY => array ( self::STREAMER_VAR => "asbody", - self::STREAMER_TYPE => "SyncBaseBody"), - - SYNC_NOTES_CATEGORIES => array ( self::STREAMER_VAR => "categories", - self::STREAMER_ARRAY => SYNC_NOTES_CATEGORY), - - SYNC_NOTES_LASTMODIFIEDDATE => array ( self::STREAMER_VAR => "lastmodified", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE), - - SYNC_NOTES_MESSAGECLASS => array ( self::STREAMER_VAR => "messageclass"), - - SYNC_NOTES_SUBJECT => array ( self::STREAMER_VAR => "subject"), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncobject.php b/sources/lib/syncobjects/syncobject.php deleted file mode 100644 index a68aba1..0000000 --- a/sources/lib/syncobjects/syncobject.php +++ /dev/null @@ -1,421 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -abstract class SyncObject extends Streamer { - const STREAMER_CHECKS = 6; - const STREAMER_CHECK_REQUIRED = 7; - const STREAMER_CHECK_ZEROORONE = 8; - const STREAMER_CHECK_NOTALLOWED = 9; - const STREAMER_CHECK_ONEVALUEOF = 10; - const STREAMER_CHECK_SETZERO = "setToValue0"; - const STREAMER_CHECK_SETONE = "setToValue1"; - const STREAMER_CHECK_SETTWO = "setToValue2"; - const STREAMER_CHECK_SETEMPTY = "setToValueEmpty"; - const STREAMER_CHECK_CMPLOWER = 13; - const STREAMER_CHECK_CMPHIGHER = 14; - const STREAMER_CHECK_LENGTHMAX = 15; - const STREAMER_CHECK_EMAIL = 16; - - protected $unsetVars; - - - public function SyncObject($mapping) { - $this->unsetVars = array(); - parent::Streamer($mapping); - } - - /** - * Sets all supported but not transmitted variables - * of this SyncObject to an "empty" value, so they are deleted when being saved - * - * @param array $supportedFields array with all supported fields, if available - * - * @access public - * @return boolean - */ - public function emptySupported($supportedFields) { - // Some devices do not send supported tag. In such a case remove all not set properties. - if (($supportedFields === false || !is_array($supportedFields) || (empty($supportedFields)))) { - if (defined('UNSET_UNDEFINED_PROPERTIES') && UNSET_UNDEFINED_PROPERTIES && ($this instanceof SyncContact || $this instanceof SyncAppointment)) { - ZLog::Write(LOGLEVEL_INFO, sprintf("%s->emptySupported(): no supported list available, emptying all not set parameters", get_class($this))); - $supportedFields = array_keys($this->mapping); - } - else { - return false; - } - } - - foreach ($supportedFields as $field) { - if (!isset($this->mapping[$field])) { - ZLog::Write(LOGLEVEL_WARN, sprintf("Field '%s' is supposed to be emptied but is not defined for '%s'", $field, get_class($this))); - continue; - } - $var = $this->mapping[$field][self::STREAMER_VAR]; - // add var to $this->unsetVars if $var is not set - if (!isset($this->$var)) - $this->unsetVars[] = $var; - } - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Supported variables to be unset: %s", implode(',', $this->unsetVars))); - return true; - } - - - /** - * Compares this a SyncObject to another. - * In case that all available mapped fields are exactly EQUAL, it returns true - * - * @see SyncObject - * @param SyncObject $odo other SyncObject - * @return boolean - */ - public function equals($odo, $log = false) { - if ($odo === false) - return false; - - // check objecttype - if (! ($odo instanceof SyncObject)) { - ZLog::Write(LOGLEVEL_DEBUG, "SyncObject->equals() the target object is not a SyncObject"); - return false; - } - - // check for mapped fields - foreach ($this->mapping as $v) { - $val = $v[self::STREAMER_VAR]; - // array of values? - if (isset($v[self::STREAMER_ARRAY])) { - // seek for differences in the arrays - if (is_array($this->$val) && is_array($odo->$val)) { - if (count(array_diff($this->$val, $odo->$val)) + count(array_diff($odo->$val, $this->$val)) > 0) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() items in array '%s' differ", $val)); - return false; - } - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() array '%s' is set in one but not the other object", $val)); - return false; - } - } - else { - if (isset($this->$val) && isset($odo->$val)) { - if ($this->$val != $odo->$val){ - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false on field '%s': '%s' != '%s'", $val, Utils::PrintAsString($this->$val), Utils::PrintAsString($odo->$val))); - return false; - } - } - else if (!isset($this->$val) && !isset($odo->$val)) { - continue; - } - else { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("SyncObject->equals() false because field '%s' is only defined at one obj: '%s' != '%s'", $val, Utils::PrintAsString(isset($this->$val)), Utils::PrintAsString(isset($odo->$val)))); - return false; - } - } - } - - return true; - } - - /** - * String representation of the object - * - * @return String - */ - public function __toString() { - $str = get_class($this) . " (\n"; - - $streamerVars = array(); - foreach ($this->mapping as $k=>$v) - $streamerVars[$v[self::STREAMER_VAR]] = (isset($v[self::STREAMER_TYPE]))?$v[self::STREAMER_TYPE]:false; - - foreach (get_object_vars($this) as $k=>$v) { - if ($k == "mapping") continue; - - if (array_key_exists($k, $streamerVars)) - $strV = "(S) "; - else - $strV = ""; - - // self::STREAMER_ARRAY ? - if (is_array($v)) { - $str .= "\t". $strV . $k ."(Array) size: " . count($v) ."\n"; - foreach ($v as $value) $str .= "\t\t". Utils::PrintAsString($value) ."\n"; - } - else if ($v instanceof SyncObject) { - $str .= "\t". $strV .$k ." => ". str_replace("\n", "\n\t\t\t", $v->__toString()) . "\n"; - } - else - $str .= "\t". $strV .$k ." => " . (isset($this->$k)? Utils::PrintAsString($this->$k) :"null") . "\n"; - } - $str .= ")"; - - return $str; - } - - /** - * Returns the properties which have to be unset on the server - * - * @access public - * @return array - */ - public function getUnsetVars() { - return $this->unsetVars; - } - - /** - * Method checks if the object has the minimum of required parameters - * and fullfills semantic dependencies - * - * General checks: - * STREAMER_CHECK_REQUIRED may have as value false (do not fix, ignore object!) or set-to-values: STREAMER_CHECK_SETZERO/ONE/TWO, STREAMER_CHECK_SETEMPTY - * STREAMER_CHECK_ZEROORONE may be 0 or 1, if none of these, set-to-values: STREAMER_CHECK_SETZERO or STREAMER_CHECK_SETONE - * STREAMER_CHECK_NOTALLOWED fails if is set - * STREAMER_CHECK_ONEVALUEOF expects an array with accepted values, fails if value is not in array - * - * Comparison: - * STREAMER_CHECK_CMPLOWER compares if the current parameter is lower as a literal or another parameter of the same object - * STREAMER_CHECK_CMPHIGHER compares if the current parameter is higher as a literal or another parameter of the same object - * - * @param boolean $logAsDebug (opt) default is false, so messages are logged in WARN log level - * - * @access public - * @return boolean - */ - public function Check($logAsDebug = false) { - // semantic checks general "turn off switch" - if (defined("DO_SEMANTIC_CHECKS") && DO_SEMANTIC_CHECKS === false) { - ZLog::Write(LOGLEVEL_DEBUG, "SyncObject->Check(): semantic checks disabled. Check your config for 'DO_SEMANTIC_CHECKS'."); - return true; - } - - $defaultLogLevel = LOGLEVEL_WARN; - - // in some cases non-false checks should not provoke a WARN log but only a DEBUG log - if ($logAsDebug) - $defaultLogLevel = LOGLEVEL_DEBUG; - - $objClass = get_class($this); - foreach ($this->mapping as $k=>$v) { - - // check sub-objects recursively - if (isset($v[self::STREAMER_TYPE]) && isset($this->$v[self::STREAMER_VAR])) { - if ($this->$v[self::STREAMER_VAR] instanceof SyncObject) { - if (! $this->$v[self::STREAMER_VAR]->Check($logAsDebug)) - return false; - } - else if (is_array($this->$v[self::STREAMER_VAR])) { - foreach ($this->$v[self::STREAMER_VAR] as $subobj) - if ($subobj instanceof SyncObject && !$subobj->Check($logAsDebug)) - return false; - } - } - - if (isset($v[self::STREAMER_CHECKS])) { - foreach ($v[self::STREAMER_CHECKS] as $rule => $condition) { - // check REQUIRED settings - if ($rule === self::STREAMER_CHECK_REQUIRED && (!isset($this->$v[self::STREAMER_VAR]) || $this->$v[self::STREAMER_VAR] === '' ) ) { - // parameter is not set but .. - // requested to set to 0 - if ($condition === self::STREAMER_CHECK_SETZERO) { - $this->$v[self::STREAMER_VAR] = 0; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to 0", $objClass, $v[self::STREAMER_VAR])); - } - // requested to be set to 1 - else if ($condition === self::STREAMER_CHECK_SETONE) { - $this->$v[self::STREAMER_VAR] = 1; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to 1", $objClass, $v[self::STREAMER_VAR])); - } - // requested to be set to 2 - else if ($condition === self::STREAMER_CHECK_SETTWO) { - $this->$v[self::STREAMER_VAR] = 2; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to 2", $objClass, $v[self::STREAMER_VAR])); - } - // requested to be set to '' - else if ($condition === self::STREAMER_CHECK_SETEMPTY) { - if (!isset($this->$v[self::STREAMER_VAR])) { - $this->$v[self::STREAMER_VAR] = ''; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to ''", $objClass, $v[self::STREAMER_VAR])); - } - } - // there is another value !== false - else if ($condition !== false) { - $this->$v[self::STREAMER_VAR] = $condition; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to '%s'", $objClass, $v[self::STREAMER_VAR], $condition)); - - } - // no fix available! - else { - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter '%s' is required but not set. Check failed!", $objClass, $v[self::STREAMER_VAR])); - return false; - } - } // end STREAMER_CHECK_REQUIRED - - - // check STREAMER_CHECK_ZEROORONE - if ($rule === self::STREAMER_CHECK_ZEROORONE && isset($this->$v[self::STREAMER_VAR])) { - if ($this->$v[self::STREAMER_VAR] != 0 && $this->$v[self::STREAMER_VAR] != 1) { - $newval = $condition === self::STREAMER_CHECK_SETZERO ? 0:1; - $this->$v[self::STREAMER_VAR] = $newval; - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): Fixed object from type %s: parameter '%s' is set to '%s' as it was not 0 or 1", $objClass, $v[self::STREAMER_VAR], $newval)); - } - }// end STREAMER_CHECK_ZEROORONE - - - // check STREAMER_CHECK_ONEVALUEOF - if ($rule === self::STREAMER_CHECK_ONEVALUEOF && isset($this->$v[self::STREAMER_VAR])) { - if (!in_array($this->$v[self::STREAMER_VAR], $condition)) { - ZLog::Write($defaultLogLevel, sprintf("SyncObject->Check(): object from type %s: parameter '%s'->'%s' is not in the range of allowed values.", $objClass, $v[self::STREAMER_VAR], $this->$v[self::STREAMER_VAR])); - return false; - } - }// end STREAMER_CHECK_ONEVALUEOF - - - // Check value compared to other value or literal - if ($rule === self::STREAMER_CHECK_CMPHIGHER || $rule === self::STREAMER_CHECK_CMPLOWER) { - if (isset($this->$v[self::STREAMER_VAR])) { - $cmp = false; - // directly compare against literals - if (is_int($condition)) { - $cmp = $condition; - } - // check for invalid compare-to - else if (!isset($this->mapping[$condition])) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("SyncObject->Check(): Can not compare parameter '%s' against the other value '%s' as it is not defined object from type %s. Please report this! Check skipped!", $objClass, $v[self::STREAMER_VAR], $condition)); - continue; - } - else { - $cmpPar = $this->mapping[$condition][self::STREAMER_VAR]; - if (isset($this->$cmpPar)) - $cmp = $this->$cmpPar; - } - - if ($cmp === false) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter '%s' can not be compared, as the comparable is not set. Check failed!", $objClass, $v[self::STREAMER_VAR])); - return false; - } - if ( ($rule == self::STREAMER_CHECK_CMPHIGHER && $this->$v[self::STREAMER_VAR] < $cmp) || - ($rule == self::STREAMER_CHECK_CMPLOWER && $this->$v[self::STREAMER_VAR] > $cmp) - ) { - - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter '%s' is %s than '%s'. Check failed!", - $objClass, - $v[self::STREAMER_VAR], - (($rule === self::STREAMER_CHECK_CMPHIGHER)?'LOWER':'HIGHER'), - ((isset($cmpPar)?$cmpPar:$condition)) )); - return false; - } - } - } // STREAMER_CHECK_CMP* - - - // check STREAMER_CHECK_LENGTHMAX - if ($rule === self::STREAMER_CHECK_LENGTHMAX && isset($this->$v[self::STREAMER_VAR])) { - - if (is_array($this->$v[self::STREAMER_VAR])) { - // implosion takes 2bytes, so we just assume ", " here - $chkstr = implode(", ", $this->$v[self::STREAMER_VAR]); - } - else - $chkstr = $this->$v[self::STREAMER_VAR]; - - if (strlen($chkstr) > $condition) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): object from type %s: parameter '%s' is longer than %d. Check failed", $objClass, $v[self::STREAMER_VAR], $condition)); - return false; - } - }// end STREAMER_CHECK_LENGTHMAX - - - // check STREAMER_CHECK_EMAIL - // if $condition is false then the check really fails. Otherwise invalid emails are removed. - // if nothing is left (all emails were false), the parameter is set to condition - if ($rule === self::STREAMER_CHECK_EMAIL && isset($this->$v[self::STREAMER_VAR])) { - if ($condition === false && ( (is_array($this->$v[self::STREAMER_VAR]) && empty($this->$v[self::STREAMER_VAR])) || strlen($this->$v[self::STREAMER_VAR]) == 0) ) - continue; - - $as_array = false; - - if (is_array($this->$v[self::STREAMER_VAR])) { - $mails = $this->$v[self::STREAMER_VAR]; - $as_array = true; - } - else { - $mails = array( $this->$v[self::STREAMER_VAR] ); - } - - $output = array(); - foreach ($mails as $mail) { - if (!Utils::CheckEmail($mail) && !Utils::CheckEmailEmptyGroup($mail)) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): object from type %s: parameter '%s' contains an invalid email address '%s'. Address is removed.", $objClass, $v[self::STREAMER_VAR], $mail)); - } - else - $output[] = $mail; - } - if (count($mails) != count($output)) { - if ($condition === false) - return false; - - // nothing left, use $condition as new value - if (count($output) == 0) - $output[] = $condition; - - // if we are allowed to rewrite the attribute, we do that - if ($as_array) - $this->$v[self::STREAMER_VAR] = $output; - else - $this->$v[self::STREAMER_VAR] = $output[0]; - } - }// end STREAMER_CHECK_EMAIL - - - } // foreach CHECKS - } // isset CHECKS - } // foreach mapping - - return true; - } -} diff --git a/sources/lib/syncobjects/syncoof.php b/sources/lib/syncobjects/syncoof.php deleted file mode 100644 index 9becfc0..0000000 --- a/sources/lib/syncobjects/syncoof.php +++ /dev/null @@ -1,82 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncOOF extends SyncObject { - public $oofstate; - public $starttime; - public $endtime; - public $oofmessage = array(); - public $bodytype; - public $Status; - - public function SyncOOF() { - $mapping = array ( - SYNC_SETTINGS_OOFSTATE => array ( self::STREAMER_VAR => "oofstate", - self::STREAMER_CHECKS => array( array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2) ))), - - SYNC_SETTINGS_STARTTIME => array ( self::STREAMER_VAR => "starttime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_SETTINGS_ENDTIME => array ( self::STREAMER_VAR => "endtime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_SETTINGS_OOFMESSAGE => array ( self::STREAMER_VAR => "oofmessage", - self::STREAMER_TYPE => "SyncOOFMessage", - self::STREAMER_PROP => self::STREAMER_TYPE_NO_CONTAINER, - self::STREAMER_ARRAY => SYNC_SETTINGS_OOFMESSAGE), - - SYNC_SETTINGS_BODYTYPE => array ( self::STREAMER_VAR => "bodytype", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(SYNC_SETTINGSOOF_BODYTYPE_HTML, ucfirst(strtolower(SYNC_SETTINGSOOF_BODYTYPE_TEXT))) )), - - SYNC_SETTINGS_PROP_STATUS => array ( self::STREAMER_VAR => "Status", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE) - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncoofmessage.php b/sources/lib/syncobjects/syncoofmessage.php deleted file mode 100644 index 88d48a5..0000000 --- a/sources/lib/syncobjects/syncoofmessage.php +++ /dev/null @@ -1,80 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncOOFMessage extends SyncObject { - public $appliesToInternal; - public $appliesToExternal; - public $appliesToExternalUnknown; - public $enabled; - public $replymessage; - public $bodytype; - - public function SyncOOFMessage() { - $mapping = array ( - //only one of the following 3 apply types will be available - SYNC_SETTINGS_APPLIESTOINTERVAL => array ( self::STREAMER_VAR => "appliesToInternal", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY), - - SYNC_SETTINGS_APPLIESTOEXTERNALKNOWN => array ( self::STREAMER_VAR => "appliesToExternal", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY), - - SYNC_SETTINGS_APPLIESTOEXTERNALUNKNOWN => array ( self::STREAMER_VAR => "appliesToExternalUnknown", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY), - - SYNC_SETTINGS_ENABLED => array ( self::STREAMER_VAR => "enabled"), - - SYNC_SETTINGS_REPLYMESSAGE => array ( self::STREAMER_VAR => "replymessage"), - - SYNC_SETTINGS_BODYTYPE => array ( self::STREAMER_VAR => "bodytype", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(SYNC_SETTINGSOOF_BODYTYPE_HTML, ucfirst(strtolower(SYNC_SETTINGSOOF_BODYTYPE_TEXT))) )), - - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncprovisioning.php b/sources/lib/syncobjects/syncprovisioning.php deleted file mode 100644 index a859a90..0000000 --- a/sources/lib/syncobjects/syncprovisioning.php +++ /dev/null @@ -1,302 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncProvisioning extends SyncObject { - //AS 12.0, 12.1 and 14.0 props - public $devpwenabled; - public $alphanumpwreq; - public $devencenabled; - public $pwrecoveryenabled; - public $docbrowseenabled; - public $attenabled; - public $mindevpwlenngth; - public $maxinacttimedevlock; - public $maxdevpwfailedattempts; - public $maxattsize; - public $allowsimpledevpw; - public $devpwexpiration; - public $devpwhistory; - - //AS 12.1 and 14.0 props - public $allostoragecard; - public $allowcam; - public $reqdevenc; - public $allowunsignedapps; - public $allowunsigninstallpacks; - public $mindevcomplexchars; - public $allowwifi; - public $allowtextmessaging; - public $allowpopimapemail; - public $allowbluetooth; - public $allowirda; - public $reqmansyncroam; - public $allowdesktopsync; - public $maxcalagefilter; - public $allowhtmlemail; - public $maxemailagefilter; - public $maxemailbodytruncsize; - public $maxemailhtmlbodytruncsize; - public $reqsignedsmimemessages; - public $reqencsmimemessages; - public $reqsignedsmimealgorithm; - public $reqencsmimealgorithm; - public $allowsmimeencalgneg; - public $allowsmimesoftcerts; - public $allowbrowser; - public $allowconsumeremail; - public $allowremotedesk; - public $allowinternetsharing; - public $unapprovedinromapplist; - public $approvedapplist; - - function SyncProvisioning() { - $mapping = array ( - SYNC_PROVISION_DEVPWENABLED => array ( self::STREAMER_VAR => "devpwenabled", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALPHANUMPWREQ => array ( self::STREAMER_VAR => "alphanumpwreq", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_PWRECOVERYENABLED => array ( self::STREAMER_VAR => "pwrecoveryenabled", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_DEVENCENABLED => array ( self::STREAMER_VAR => "devencenabled", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_DOCBROWSEENABLED => array ( self::STREAMER_VAR => "docbrowseenabled"), // depricated - SYNC_PROVISION_ATTENABLED => array ( self::STREAMER_VAR => "attenabled", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_MINDEVPWLENGTH => array ( self::STREAMER_VAR => "mindevpwlenngth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 17 )), - - SYNC_PROVISION_MAXINACTTIMEDEVLOCK => array ( self::STREAMER_VAR => "maxinacttimedevlock"), - SYNC_PROVISION_MAXDEVPWFAILEDATTEMPTS => array ( self::STREAMER_VAR => "maxdevpwfailedattempts", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 3, - self::STREAMER_CHECK_CMPLOWER => 17 )), - - SYNC_PROVISION_MAXATTSIZE => array ( self::STREAMER_VAR => "maxattsize", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY, - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1 )), - - SYNC_PROVISION_ALLOWSIMPLEDEVPW => array ( self::STREAMER_VAR => "allowsimpledevpw", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_DEVPWEXPIRATION => array ( self::STREAMER_VAR => "devpwexpiration", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1 )), - - SYNC_PROVISION_DEVPWHISTORY => array ( self::STREAMER_VAR => "devpwhistory", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1 )), - ); - - if(Request::GetProtocolVersion() >= 12.1) { - $mapping += array ( - SYNC_PROVISION_ALLOWSTORAGECARD => array ( self::STREAMER_VAR => "allostoragecard", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWCAM => array ( self::STREAMER_VAR => "allowcam", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_REQDEVENC => array ( self::STREAMER_VAR => "reqdevenc", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWUNSIGNEDAPPS => array ( self::STREAMER_VAR => "allowunsignedapps", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWUNSIGNEDINSTALLATIONPACKAGES => array ( self::STREAMER_VAR => "allowunsigninstallpacks", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_MINDEVPWCOMPLEXCHARS => array ( self::STREAMER_VAR => "mindevcomplexchars", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4) )), - - SYNC_PROVISION_ALLOWWIFI => array ( self::STREAMER_VAR => "allowwifi", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWTEXTMESSAGING => array ( self::STREAMER_VAR => "allowtextmessaging", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWPOPIMAPEMAIL => array ( self::STREAMER_VAR => "allowpopimapemail", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWBLUETOOTH => array ( self::STREAMER_VAR => "allowbluetooth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2) )), - - SYNC_PROVISION_ALLOWIRDA => array ( self::STREAMER_VAR => "allowirda", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_REQMANUALSYNCWHENROAM => array ( self::STREAMER_VAR => "reqmansyncroam", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWDESKTOPSYNC => array ( self::STREAMER_VAR => "allowdesktopsync", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_MAXCALAGEFILTER => array ( self::STREAMER_VAR => "maxcalagefilter", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,4,5,6,7) )), - - SYNC_PROVISION_ALLOWHTMLEMAIL => array ( self::STREAMER_VAR => "allowhtmlemail", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_MAXEMAILAGEFILTER => array ( self::STREAMER_VAR => "maxemailagefilter", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1, - self::STREAMER_CHECK_CMPLOWER => 6 )), - - SYNC_PROVISION_MAXEMAILBODYTRUNCSIZE => array ( self::STREAMER_VAR => "maxemailbodytruncsize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -2 )), - - SYNC_PROVISION_MAXEMAILHTMLBODYTRUNCSIZE => array ( self::STREAMER_VAR => "maxemailhtmlbodytruncsize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -2 )), - - SYNC_PROVISION_REQSIGNEDSMIMEMESSAGES => array ( self::STREAMER_VAR => "reqsignedsmimemessages", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_REQENCSMIMEMESSAGES => array ( self::STREAMER_VAR => "reqencsmimemessages", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_REQSIGNEDSMIMEALGORITHM => array ( self::STREAMER_VAR => "reqsignedsmimealgorithm", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_REQENCSMIMEALGORITHM => array ( self::STREAMER_VAR => "reqencsmimealgorithm", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,4) )), - - SYNC_PROVISION_ALLOWSMIMEENCALGORITHNEG => array ( self::STREAMER_VAR => "allowsmimeencalgneg", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2) )), - - SYNC_PROVISION_ALLOWSMIMESOFTCERTS => array ( self::STREAMER_VAR => "allowsmimesoftcerts", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWBROWSER => array ( self::STREAMER_VAR => "allowbrowser", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWCONSUMEREMAIL => array ( self::STREAMER_VAR => "allowconsumeremail", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWREMOTEDESKTOP => array ( self::STREAMER_VAR => "allowremotedesk", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_ALLOWINTERNETSHARING => array ( self::STREAMER_VAR => "allowinternetsharing", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1) )), - - SYNC_PROVISION_UNAPPROVEDINROMAPPLIST => array ( self::STREAMER_VAR => "unapprovedinromapplist", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY, - self::STREAMER_ARRAY => SYNC_PROVISION_APPNAME), //TODO check - - SYNC_PROVISION_APPROVEDAPPLIST => array ( self::STREAMER_VAR => "approvedapplist", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY, - self::STREAMER_ARRAY => SYNC_PROVISION_HASH), //TODO check - ); - } - - parent::SyncObject($mapping); - } - - public function Load($policies = array()) { - if (empty($policies)) { - $this->LoadDefaultPolicies(); - } - else foreach ($policies as $p=>$v) { - if (!isset($this->mapping[$p])) { - ZLog::Write(LOGLEVEL_INFO, sprintf("Policy '%s' not supported by the device, ignoring", substr($p, strpos($p,':')+1))); - continue; - } - ZLog::Write(LOGLEVEL_INFO, sprintf("Policy '%s' enforced with: %s", substr($p, strpos($p,':')+1), Utils::PrintAsString($v))); - - $var = $this->mapping[$p][self::STREAMER_VAR]; - $this->$var = $v; - } - } - - public function LoadDefaultPolicies() { - //AS 12.0, 12.1 and 14.0 props - $this->devpwenabled = 0; - $this->alphanumpwreq = 0; - $this->devencenabled = 0; - $this->pwrecoveryenabled = 0; - $this->docbrowseenabled; - $this->attenabled = 1; - $this->mindevpwlenngth = 4; - $this->maxinacttimedevlock = 900; - $this->maxdevpwfailedattempts = 8; - $this->maxattsize = ''; - $this->allowsimpledevpw = 1; - $this->devpwexpiration = 0; - $this->devpwhistory = 0; - - //AS 12.1 and 14.0 props - $this->allostoragecard = 1; - $this->allowcam = 1; - $this->reqdevenc = 0; - $this->allowunsignedapps = 1; - $this->allowunsigninstallpacks = 1; - $this->mindevcomplexchars = 3; - $this->allowwifi = 1; - $this->allowtextmessaging = 1; - $this->allowpopimapemail = 1; - $this->allowbluetooth = 2; - $this->allowirda = 1; - $this->reqmansyncroam = 0; - $this->allowdesktopsync = 1; - $this->maxcalagefilter = 0; - $this->allowhtmlemail = 1; - $this->maxemailagefilter = 0; - $this->maxemailbodytruncsize = -1; - $this->maxemailhtmlbodytruncsize = -1; - $this->reqsignedsmimemessages = 0; - $this->reqencsmimemessages = 0; - $this->reqsignedsmimealgorithm = 0; - $this->reqencsmimealgorithm = 0; - $this->allowsmimeencalgneg = 2; - $this->allowsmimesoftcerts = 1; - $this->allowbrowser = 1; - $this->allowconsumeremail = 1; - $this->allowremotedesk = 1; - $this->allowinternetsharing = 1; - $this->unapprovedinromapplist = array(); - $this->approvedapplist = array(); - } -} diff --git a/sources/lib/syncobjects/syncrecurrence.php b/sources/lib/syncobjects/syncrecurrence.php deleted file mode 100644 index cc2c0a4..0000000 --- a/sources/lib/syncobjects/syncrecurrence.php +++ /dev/null @@ -1,119 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncRecurrence extends SyncObject { - public $type; - public $until; - public $occurrences; - public $interval; - public $dayofweek; - public $dayofmonth; - public $weekofmonth; - public $monthofyear; - - function SyncRecurrence() { - $mapping = array ( - // Recurrence type - // 0 = Recurs daily - // 1 = Recurs weekly - // 2 = Recurs monthly - // 3 = Recurs monthly on the nth day - // 5 = Recurs yearly - // 6 = Recurs yearly on the nth day - SYNC_POOMCAL_TYPE => array ( self::STREAMER_VAR => "type", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,5,6) )), - - SYNC_POOMCAL_UNTIL => array ( self::STREAMER_VAR => "until", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE), - - SYNC_POOMCAL_OCCURRENCES => array ( self::STREAMER_VAR => "occurrences", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - SYNC_POOMCAL_INTERVAL => array ( self::STREAMER_VAR => "interval", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - // DayOfWeek values - // 1 = Sunday - // 2 = Monday - // 4 = Tuesday - // 8 = Wednesday - // 16 = Thursday - // 32 = Friday - // 62 = Weekdays // not in spec: daily weekday recurrence - // 64 = Saturday - // 127 = The last day of the month. Value valid only in monthly or yearly recurrences. - // As this is a bitmask, actually all values 0 > x < 128 are allowed - SYNC_POOMCAL_DAYOFWEEK => array ( self::STREAMER_VAR => "dayofweek", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 128 )), - - // DayOfMonth values - // 1-31 representing the day - SYNC_POOMCAL_DAYOFMONTH => array ( self::STREAMER_VAR => "dayofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 32 )), - - // WeekOfMonth - // 1-4 = Y st/nd/rd/th week of month - // 5 = last week of month - SYNC_POOMCAL_WEEKOFMONTH => array ( self::STREAMER_VAR => "weekofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5) )), - - // MonthOfYear - // 1-12 representing the month - SYNC_POOMCAL_MONTHOFYEAR => array ( self::STREAMER_VAR => "monthofyear", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5,6,7,8,9,10,11,12) )), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncresolverecipient.php b/sources/lib/syncobjects/syncresolverecipient.php deleted file mode 100755 index 81b6ae3..0000000 --- a/sources/lib/syncobjects/syncresolverecipient.php +++ /dev/null @@ -1,76 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncResolveRecipient extends SyncObject { - public $type; - public $displayname; - public $emailaddress; - public $availability; - public $certificates; - public $pictures; - - public function SyncResolveRecipient() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_TYPE => array ( self::STREAMER_VAR => "type"), - SYNC_RESOLVERECIPIENTS_DISPLAYNAME => array ( self::STREAMER_VAR => "displayname"), - SYNC_RESOLVERECIPIENTS_EMAILADDRESS => array ( self::STREAMER_VAR => "emailaddress"), - - SYNC_RESOLVERECIPIENTS_AVAILABILITY => array ( self::STREAMER_VAR => "availability", - self::STREAMER_TYPE => "SyncRRAvailability"), - - SYNC_RESOLVERECIPIENTS_CERTIFICATES => array ( self::STREAMER_VAR => "certificates", - self::STREAMER_TYPE => "SyncRRCertificates"), - - SYNC_RESOLVERECIPIENTS_PICTURE => array ( self::STREAMER_VAR => "pictures", - self::STREAMER_TYPE => "SyncRRPicture", - self::STREAMER_ARRAY => SYNC_RESOLVERECIPIENTS_PICTURE), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncresolverecipients.php b/sources/lib/syncobjects/syncresolverecipients.php deleted file mode 100755 index 12cb30d..0000000 --- a/sources/lib/syncobjects/syncresolverecipients.php +++ /dev/null @@ -1,74 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncResolveRecipients extends SyncObject { - public $to = array(); - public $options; - public $status; - public $recipientCount; - public $recipient; - - public function SyncResolveRecipients() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_TO => array ( self::STREAMER_VAR => "to", - self::STREAMER_ARRAY => SYNC_RESOLVERECIPIENTS_TO, - self::STREAMER_PROP => self::STREAMER_TYPE_NO_CONTAINER), - - SYNC_RESOLVERECIPIENTS_OPTIONS => array ( self::STREAMER_VAR => "options", - self::STREAMER_TYPE => "SyncRROptions"), - - SYNC_RESOLVERECIPIENTS_STATUS => array ( self::STREAMER_VAR => "status"), - SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT => array ( self::STREAMER_VAR => "recipientcount"), - - SYNC_RESOLVERECIPIENTS_RECIPIENT => array ( self::STREAMER_VAR => "recipient", - self::STREAMER_TYPE => "SyncResolveRecipient"), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncresolverecipientsavailability.php b/sources/lib/syncobjects/syncresolverecipientsavailability.php deleted file mode 100755 index 5dae42e..0000000 --- a/sources/lib/syncobjects/syncresolverecipientsavailability.php +++ /dev/null @@ -1,65 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncRRAvailability extends SyncObject { - public $starttime; - public $endtime; - public $status; - public $mergedfreebusy; - - public function SyncRRAvailability() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_STARTTIME => array ( self::STREAMER_VAR => "starttime"), - SYNC_RESOLVERECIPIENTS_ENDTIME => array ( self::STREAMER_VAR => "endtime"), - SYNC_RESOLVERECIPIENTS_STATUS => array ( self::STREAMER_VAR => "status"), - SYNC_RESOLVERECIPIENTS_MERGEDFREEBUSY => array ( self::STREAMER_VAR => "mergedfreebusy"), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncresolverecipientscertificates.php b/sources/lib/syncobjects/syncresolverecipientscertificates.php deleted file mode 100755 index 6496f65..0000000 --- a/sources/lib/syncobjects/syncresolverecipientscertificates.php +++ /dev/null @@ -1,73 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncRRCertificates extends SyncObject { - public $status; - public $certificatecount; - public $recipientcount; - public $certificate; - public $minicertificate; - - public function SyncRRCertificates() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_STATUS => array ( self::STREAMER_VAR => "status"), - SYNC_RESOLVERECIPIENTS_CERTIFICATECOUNT => array ( self::STREAMER_VAR => "certificatecount"), - SYNC_RESOLVERECIPIENTS_RECIPIENTCOUNT => array ( self::STREAMER_VAR => "recipientcount"), - - SYNC_RESOLVERECIPIENTS_CERTIFICATE => array ( self::STREAMER_VAR => "certificate", - self::STREAMER_ARRAY => SYNC_RESOLVERECIPIENTS_CERTIFICATE, - self::STREAMER_PROP => self::STREAMER_TYPE_NO_CONTAINER), - - SYNC_RESOLVERECIPIENTS_MINICERTIFICATE => array ( self::STREAMER_VAR => "minicertificate", - self::STREAMER_ARRAY => SYNC_RESOLVERECIPIENTS_MINICERTIFICATE, - self::STREAMER_PROP => self::STREAMER_TYPE_NO_CONTAINER) - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncresolverecipientsoptions.php b/sources/lib/syncobjects/syncresolverecipientsoptions.php deleted file mode 100755 index 726350f..0000000 --- a/sources/lib/syncobjects/syncresolverecipientsoptions.php +++ /dev/null @@ -1,71 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncRROptions extends SyncObject { - public $certificateretrieval; - public $maxcertificates; - public $maxambiguousrecipients; - public $availability; - public $picture; - - public function SyncRROptions() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_CERTIFICATERETRIEVAL => array ( self::STREAMER_VAR => "certificateretrieval"), - SYNC_RESOLVERECIPIENTS_MAXCERTIFICATES => array ( self::STREAMER_VAR => "maxcertificates"), - SYNC_RESOLVERECIPIENTS_MAXAMBIGUOUSRECIPIENTS => array ( self::STREAMER_VAR => "maxambiguousrecipients"), - - SYNC_RESOLVERECIPIENTS_AVAILABILITY => array ( self::STREAMER_VAR => "availability", - self::STREAMER_TYPE => "SyncRRAvailability"), - - SYNC_RESOLVERECIPIENTS_PICTURE => array ( self::STREAMER_VAR => "picture", - self::STREAMER_TYPE => "SyncRRPicture"), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncresolverecipientspicture.php b/sources/lib/syncobjects/syncresolverecipientspicture.php deleted file mode 100755 index 7c18a63..0000000 --- a/sources/lib/syncobjects/syncresolverecipientspicture.php +++ /dev/null @@ -1,65 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncRRPicture extends SyncObject { - public $maxsize; - public $maxpictures; - public $status; - public $data; - - public function SyncRRPicture() { - $mapping = array ( - SYNC_RESOLVERECIPIENTS_MAXSIZE => array ( self::STREAMER_VAR => "maxsize"), - SYNC_RESOLVERECIPIENTS_MAXPICTURES => array ( self::STREAMER_VAR => "maxpictures"), - SYNC_RESOLVERECIPIENTS_STATUS => array ( self::STREAMER_VAR => "status"), - SYNC_RESOLVERECIPIENTS_DATA => array ( self::STREAMER_VAR => "data"), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/syncsendmail.php b/sources/lib/syncobjects/syncsendmail.php deleted file mode 100644 index c884af0..0000000 --- a/sources/lib/syncobjects/syncsendmail.php +++ /dev/null @@ -1,85 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncSendMail extends SyncObject { - public $clientid; - public $saveinsent; - public $replacemime; - public $accountid; - public $source; - public $mime; - public $replyflag; - public $forwardflag; - - function SyncSendMail() { - $mapping = array ( - SYNC_COMPOSEMAIL_CLIENTID => array ( self::STREAMER_VAR => "clientid"), - - SYNC_COMPOSEMAIL_SAVEINSENTITEMS => array ( self::STREAMER_VAR => "saveinsent", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY), - - SYNC_COMPOSEMAIL_REPLACEMIME => array ( self::STREAMER_VAR => "replacemime", - self::STREAMER_PROP => self::STREAMER_TYPE_SEND_EMPTY), - - SYNC_COMPOSEMAIL_ACCOUNTID => array ( self::STREAMER_VAR => "accountid"), - - SYNC_COMPOSEMAIL_SOURCE => array ( self::STREAMER_VAR => "source", - self::STREAMER_TYPE => "SyncSendMailSource"), - - SYNC_COMPOSEMAIL_MIME => array ( self::STREAMER_VAR => "mime"), - - SYNC_COMPOSEMAIL_REPLYFLAG => array ( self::STREAMER_VAR => "replyflag", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE), - - SYNC_COMPOSEMAIL_FORWARDFLAG => array ( self::STREAMER_VAR => "forwardflag", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE), - ); - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncsendmailsource.php b/sources/lib/syncobjects/syncsendmailsource.php deleted file mode 100644 index a0d3d00..0000000 --- a/sources/lib/syncobjects/syncsendmailsource.php +++ /dev/null @@ -1,66 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncSendMailSource extends SyncObject { - public $folderid; - public $itemid; - public $longid; - public $instanceid; - - function SyncSendMailSource() { - $mapping = array ( - SYNC_COMPOSEMAIL_FOLDERID => array ( self::STREAMER_VAR => "folderid"), - SYNC_COMPOSEMAIL_ITEMID => array ( self::STREAMER_VAR => "itemid"), - SYNC_COMPOSEMAIL_LONGID => array ( self::STREAMER_VAR => "longid"), - SYNC_COMPOSEMAIL_INSTANCEID => array ( self::STREAMER_VAR => "instanceid"), - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/syncobjects/synctask.php b/sources/lib/syncobjects/synctask.php deleted file mode 100644 index 3a52670..0000000 --- a/sources/lib/syncobjects/synctask.php +++ /dev/null @@ -1,186 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class SyncTask extends SyncObject { - // AS 2.5 props - public $body; - public $bodysize; - public $bodytruncated; - // - public $complete; - public $datecompleted; - public $duedate; - public $utcduedate; - public $importance; - public $recurrence; - public $regenerate; - public $deadoccur; - public $reminderset; - public $remindertime; - public $sensitivity; - public $startdate; - public $utcstartdate; - public $subject; - public $rtf; - public $categories; - // AS 12.0 props - public $asbody; - - function SyncTask() { - $mapping = array ( - SYNC_POOMTASKS_COMPLETE => array ( self::STREAMER_VAR => "complete", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO )), - - SYNC_POOMTASKS_DATECOMPLETED => array ( self::STREAMER_VAR => "datecompleted", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_DUEDATE => array ( self::STREAMER_VAR => "duedate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_UTCDUEDATE => array ( self::STREAMER_VAR => "utcduedate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - // Importance values - // 0 = Low - // 1 = Normal - // 2 = High - // even the default value 1 is optional, the native android client 2.2 interprets a non-existing value as 0 (low) - SYNC_POOMTASKS_IMPORTANCE => array ( self::STREAMER_VAR => "importance", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETONE, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2) )), - - SYNC_POOMTASKS_RECURRENCE => array ( self::STREAMER_VAR => "recurrence", - self::STREAMER_TYPE => "SyncTaskRecurrence"), - - SYNC_POOMTASKS_REGENERATE => array ( self::STREAMER_VAR => "regenerate"), - SYNC_POOMTASKS_DEADOCCUR => array ( self::STREAMER_VAR => "deadoccur"), - SYNC_POOMTASKS_REMINDERSET => array ( self::STREAMER_VAR => "reminderset", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO )), - - SYNC_POOMTASKS_REMINDERTIME => array ( self::STREAMER_VAR => "remindertime", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - // Sensitivity values - // 0 = Normal - // 1 = Personal - // 2 = Private - // 3 = Confident - SYNC_POOMTASKS_SENSITIVITY => array ( self::STREAMER_VAR => "sensitivity", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3) )), - - SYNC_POOMTASKS_STARTDATE => array ( self::STREAMER_VAR => "startdate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_UTCSTARTDATE => array ( self::STREAMER_VAR => "utcstartdate", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE_DASHES), - - SYNC_POOMTASKS_SUBJECT => array ( self::STREAMER_VAR => "subject"), - SYNC_POOMTASKS_RTF => array ( self::STREAMER_VAR => "rtf"), - SYNC_POOMTASKS_CATEGORIES => array ( self::STREAMER_VAR => "categories", - self::STREAMER_ARRAY => SYNC_POOMTASKS_CATEGORY), - ); - - if (Request::GetProtocolVersion() >= 12.0) { - $mapping[SYNC_AIRSYNCBASE_BODY] = array ( self::STREAMER_VAR => "asbody", - self::STREAMER_TYPE => "SyncBaseBody"); - } else { - $mapping[SYNC_POOMTASKS_BODY] = array ( self::STREAMER_VAR => "body"); - $mapping[SYNC_POOMTASKS_BODYTRUNCATED] = array ( self::STREAMER_VAR => "bodytruncated", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ZEROORONE => self::STREAMER_CHECK_SETZERO)); - $mapping[SYNC_POOMTASKS_BODYSIZE] = array ( self::STREAMER_VAR => "bodysize", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => -1)); - } - - parent::SyncObject($mapping); - } - - /** - * Method checks if the object has the minimum of required parameters - * and fullfills semantic dependencies - * - * This overloads the general check() with special checks to be executed - * - * @param boolean $logAsDebug (opt) default is false, so messages are logged in WARN log level - * - * @access public - * @return boolean - */ - public function Check($logAsDebug = false) { - $ret = parent::Check($logAsDebug); - - // semantic checks general "turn off switch" - if (defined("DO_SEMANTIC_CHECKS") && DO_SEMANTIC_CHECKS === false) - return $ret; - - if (!$ret) - return false; - - if (isset($this->startdate) && isset($this->duedate) && $this->duedate < $this->startdate) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter 'startdate' is HIGHER than 'duedate'. Check failed!", get_class($this) )); - return false; - } - - if (isset($this->utcstartdate) && isset($this->utcduedate) && $this->utcduedate < $this->utcstartdate) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter 'utcstartdate' is HIGHER than 'utcduedate'. Check failed!", get_class($this) )); - return false; - } - - if (isset($this->duedate) && $this->duedate != Utils::getDayStartOfTimestamp($this->duedate)) { - $this->duedate = Utils::getDayStartOfTimestamp($this->duedate); - ZLog::Write(LOGLEVEL_DEBUG, "Set the due time to the start of the day"); - if (isset($this->startdate) && $this->duedate < $this->startdate) { - $this->startdate = Utils::getDayStartOfTimestamp($this->startdate); - ZLog::Write(LOGLEVEL_DEBUG, "Set the start date to the start of the day"); - } - } - - return true; - } -} diff --git a/sources/lib/syncobjects/synctaskrecurrence.php b/sources/lib/syncobjects/synctaskrecurrence.php deleted file mode 100644 index 63a1074..0000000 --- a/sources/lib/syncobjects/synctaskrecurrence.php +++ /dev/null @@ -1,157 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -// Exactly the same as SyncRecurrence, but then with SYNC_POOMTASKS_* -class SyncTaskRecurrence extends SyncObject { - public $start; - public $type; - public $until; - public $occurrences; - public $interval; - public $dayofweek; - public $dayofmonth; - public $weekofmonth; - public $monthofyear; - public $regenerate; - public $deadoccur; - - function SyncTaskRecurrence() { - $mapping = array ( - SYNC_POOMTASKS_START => array ( self::STREAMER_VAR => "start", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE), - - // Recurrence type - // 0 = Recurs daily - // 1 = Recurs weekly - // 2 = Recurs monthly - // 3 = Recurs monthly on the nth day - // 5 = Recurs yearly - // 6 = Recurs yearly on the nth day - SYNC_POOMTASKS_TYPE => array ( self::STREAMER_VAR => "type", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_REQUIRED => self::STREAMER_CHECK_SETZERO, - self::STREAMER_CHECK_ONEVALUEOF => array(0,1,2,3,5,6) )), - - SYNC_POOMTASKS_UNTIL => array ( self::STREAMER_VAR => "until", - self::STREAMER_TYPE => self::STREAMER_TYPE_DATE ), - - SYNC_POOMTASKS_OCCURRENCES => array ( self::STREAMER_VAR => "occurrences", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - SYNC_POOMTASKS_INTERVAL => array ( self::STREAMER_VAR => "interval", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 1000 )), - - //TODO: check iOS5 sends deadoccur inside of the recurrence - SYNC_POOMTASKS_DEADOCCUR => array ( self::STREAMER_VAR => "deadoccur"), - SYNC_POOMTASKS_REGENERATE => array ( self::STREAMER_VAR => "regenerate"), - - // DayOfWeek values - // 1 = Sunday - // 2 = Monday - // 4 = Tuesday - // 8 = Wednesday - // 16 = Thursday - // 32 = Friday - // 62 = Weekdays // TODO check: value set by WA with daily weekday recurrence - // 64 = Saturday - // 127 = The last day of the month. Value valid only in monthly or yearly recurrences. - SYNC_POOMTASKS_DAYOFWEEK => array ( self::STREAMER_VAR => "dayofweek", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 128 )), - - // DayOfMonth values - // 1-31 representing the day - SYNC_POOMTASKS_DAYOFMONTH => array ( self::STREAMER_VAR => "dayofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_CMPHIGHER => 0, - self::STREAMER_CHECK_CMPLOWER => 32 )), - - // WeekOfMonth - // 1-4 = Y st/nd/rd/th week of month - // 5 = last week of month - SYNC_POOMTASKS_WEEKOFMONTH => array ( self::STREAMER_VAR => "weekofmonth", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5) )), - - // MonthOfYear - // 1-12 representing the month - SYNC_POOMTASKS_MONTHOFYEAR => array ( self::STREAMER_VAR => "monthofyear", - self::STREAMER_CHECKS => array( self::STREAMER_CHECK_ONEVALUEOF => array(1,2,3,4,5,6,7,8,9,10,11,12) )), - - ); - parent::SyncObject($mapping); - } - - /** - * Method checks if the object has the minimum of required parameters - * and fullfills semantic dependencies - * - * This overloads the general check() with special checks to be executed - * - * @param boolean $logAsDebug (opt) default is false, so messages are logged in WARN log level - * - * @access public - * @return boolean - */ - public function Check($logAsDebug = false) { - $ret = parent::Check($logAsDebug); - - // semantic checks general "turn off switch" - if (defined("DO_SEMANTIC_CHECKS") && DO_SEMANTIC_CHECKS === false) - return $ret; - - if (!$ret) - return false; - - if (isset($this->start) && isset($this->until) && $this->until < $this->start) { - ZLog::Write(LOGLEVEL_WARN, sprintf("SyncObject->Check(): Unmet condition in object from type %s: parameter 'start' is HIGHER than 'until'. Check failed!", get_class($this) )); - return false; - } - - return true; - } -} diff --git a/sources/lib/syncobjects/syncuserinformation.php b/sources/lib/syncobjects/syncuserinformation.php deleted file mode 100644 index 2fac953..0000000 --- a/sources/lib/syncobjects/syncuserinformation.php +++ /dev/null @@ -1,77 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncUserInformation extends SyncObject { - public $accountid; - public $accountname; - public $userdisplayname; - public $senddisabled; - public $emailaddresses; - public $Status; - - public function SyncUserInformation() { - $mapping = array ( - SYNC_SETTINGS_ACCOUNTID => array ( self::STREAMER_VAR => "accountid"), - SYNC_SETTINGS_ACCOUNTNAME => array ( self::STREAMER_VAR => "accountname"), - SYNC_SETTINGS_EMAILADDRESSES => array ( self::STREAMER_VAR => "emailaddresses", - self::STREAMER_ARRAY => SYNC_SETTINGS_SMPTADDRESS), - - SYNC_SETTINGS_PROP_STATUS => array ( self::STREAMER_VAR => "Status", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE) - ); - - if (Request::GetProtocolVersion() >= 12.1) { - $mapping[SYNC_SETTINGS_USERDISPLAYNAME] = array ( self::STREAMER_VAR => "userdisplayname"); - } - - if (Request::GetProtocolVersion() >= 14.0) { - $mapping[SYNC_SETTINGS_SENDDISABLED] = array ( self::STREAMER_VAR => "senddisabled"); - } - - parent::SyncObject($mapping); - } -} diff --git a/sources/lib/syncobjects/syncvalidatecert.php b/sources/lib/syncobjects/syncvalidatecert.php deleted file mode 100755 index 08ece86..0000000 --- a/sources/lib/syncobjects/syncvalidatecert.php +++ /dev/null @@ -1,71 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class SyncValidateCert extends SyncObject { - public $certificatechain; - public $certificates; - public $checkCRL; - public $Status; - - public function SyncValidateCert() { - $mapping = array ( - SYNC_VALIDATECERT_CERTIFICATECHAIN => array ( self::STREAMER_VAR => "certificatechain", - self::STREAMER_ARRAY => SYNC_VALIDATECERT_CERTIFICATE), - - SYNC_VALIDATECERT_CERTIFICATES => array ( self::STREAMER_VAR => "certificates", - self::STREAMER_ARRAY => SYNC_VALIDATECERT_CERTIFICATE), - - SYNC_VALIDATECERT_CHECKCRL => array ( self::STREAMER_VAR => "checkCRL"), - - SYNC_SETTINGS_PROP_STATUS => array ( self::STREAMER_VAR => "Status", - self::STREAMER_TYPE => self::STREAMER_TYPE_IGNORE) - ); - - parent::SyncObject($mapping); - } - -} diff --git a/sources/lib/utils/compat.php b/sources/lib/utils/compat.php deleted file mode 100644 index 9a68868..0000000 --- a/sources/lib/utils/compat.php +++ /dev/null @@ -1,118 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -if (!function_exists("hex2bin")) { - /** - * Complementary function to bin2hex() which converts a hex entryid to a binary entryid. - * Since PHP 5.4 an internal hex2bin() implementation is available. - * - * @param string $data the hexadecimal string - * - * @returns string - */ - function hex2bin($data) { - return pack("H*", $data); - } -} - -if (!function_exists('http_response_code')) { - /** - * http_response_code does not exists in PHP < 5.4 - * http://php.net/manual/en/function.http-response-code.php - */ - function http_response_code($code = NULL) { - if ($code !== NULL) { - switch ($code) { - case 100: $text = 'Continue'; break; - case 101: $text = 'Switching Protocols'; break; - case 200: $text = 'OK'; break; - case 201: $text = 'Created'; break; - case 202: $text = 'Accepted'; break; - case 203: $text = 'Non-Authoritative Information'; break; - case 204: $text = 'No Content'; break; - case 205: $text = 'Reset Content'; break; - case 206: $text = 'Partial Content'; break; - case 300: $text = 'Multiple Choices'; break; - case 301: $text = 'Moved Permanently'; break; - case 302: $text = 'Moved Temporarily'; break; - case 303: $text = 'See Other'; break; - case 304: $text = 'Not Modified'; break; - case 305: $text = 'Use Proxy'; break; - case 400: $text = 'Bad Request'; break; - case 401: $text = 'Unauthorized'; break; - case 402: $text = 'Payment Required'; break; - case 403: $text = 'Forbidden'; break; - case 404: $text = 'Not Found'; break; - case 405: $text = 'Method Not Allowed'; break; - case 406: $text = 'Not Acceptable'; break; - case 407: $text = 'Proxy Authentication Required'; break; - case 408: $text = 'Request Time-out'; break; - case 409: $text = 'Conflict'; break; - case 410: $text = 'Gone'; break; - case 411: $text = 'Length Required'; break; - case 412: $text = 'Precondition Failed'; break; - case 413: $text = 'Request Entity Too Large'; break; - case 414: $text = 'Request-URI Too Large'; break; - case 415: $text = 'Unsupported Media Type'; break; - case 500: $text = 'Internal Server Error'; break; - case 501: $text = 'Not Implemented'; break; - case 502: $text = 'Bad Gateway'; break; - case 503: $text = 'Service Unavailable'; break; - case 504: $text = 'Gateway Time-out'; break; - case 505: $text = 'HTTP Version not supported'; break; - default: - exit('Unknown http status code "' . htmlentities($code) . '"'); - break; - } - - $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0'); - header($protocol . ' ' . $code . ' ' . $text); - - $GLOBALS['http_response_code'] = $code; - } else { - $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200); - } - - return $code; - } -} \ No newline at end of file diff --git a/sources/lib/utils/stringstreamwrapper.php b/sources/lib/utils/stringstreamwrapper.php deleted file mode 100644 index 64a7d80..0000000 --- a/sources/lib/utils/stringstreamwrapper.php +++ /dev/null @@ -1,65 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class StringStreamWrapper { - /** - * Instantiates a stream - * - * @param string $string The string to be wrapped - * @access public - * @return stream - */ - public static function Open($string) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("StringStreamWrapper::Open(): len = %d", strlen($string))); - if (defined('BUG68532FIXED') && BUG68532FIXED === true) { - ZLog::Write(LOGLEVEL_DEBUG, "StringStreamWrapper::Open(): Using php://temp"); - $stream = fopen('php://temp', 'r+'); - } else { - ZLog::Write(LOGLEVEL_DEBUG, "StringStreamWrapper::Open(): Using tmpfile()"); - $stream = tmpfile(); - } - fwrite($stream, $string); - rewind($stream); - return $stream; - } -} diff --git a/sources/lib/utils/timezoneutil.php b/sources/lib/utils/timezoneutil.php deleted file mode 100644 index 7d814e2..0000000 --- a/sources/lib/utils/timezoneutil.php +++ /dev/null @@ -1,1320 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class TimezoneUtil { - - /** - * list of MS and AS compatible timezones - * - * origin: http://msdn.microsoft.com/en-us/library/ms912391%28v=winembedded.11%29.aspx - * dots of tz identifiers were removed - * - * Updated at: 01.06.2012 - */ - private static $mstzones = array( - "000" => array("Dateline Standard Time", "(GMT-12:00) International Date Line West"), - "001" => array("Samoa Standard Time", "(GMT-11:00) Midway Island, Samoa"), - "002" => array("Hawaiian Standard Time", "(GMT-10:00) Hawaii"), - "003" => array("Alaskan Standard Time", "(GMT-09:00) Alaska"), - "004" => array("Pacific Standard Time", "(GMT-08:00) Pacific Time (US and Canada); Tijuana"), - "010" => array("Mountain Standard Time", "(GMT-07:00) Mountain Time (US and Canada)"), - "013" => array("Mexico Standard Time 2", "(GMT-07:00) Chihuahua, La Paz, Mazatlan"), - "015" => array("US Mountain Standard Time", "(GMT-07:00) Arizona"), - "020" => array("Central Standard Time", "(GMT-06:00) Central Time (US and Canada"), - "025" => array("Canada Central Standard Time", "(GMT-06:00) Saskatchewan"), - "030" => array("Mexico Standard Time", "(GMT-06:00) Guadalajara, Mexico City, Monterrey"), - "033" => array("Central America Standard Time", "(GMT-06:00) Central America"), - "035" => array("Eastern Standard Time", "(GMT-05:00) Eastern Time (US and Canada)"), - "040" => array("US Eastern Standard Time", "(GMT-05:00) Indiana (East)"), - "045" => array("SA Pacific Standard Time", "(GMT-05:00) Bogota, Lima, Quito"), - "uk1" => array("Venezuela Standard Time", "(GMT-04:30) Caracas"), // added - "050" => array("Atlantic Standard Time", "(GMT-04:00) Atlantic Time (Canada)"), - "055" => array("SA Western Standard Time", "(GMT-04:00) Caracas, La Paz"), - "056" => array("Pacific SA Standard Time", "(GMT-04:00) Santiago"), - "060" => array("Newfoundland and Labrador Standard Time", "(GMT-03:30) Newfoundland and Labrador"), - "065" => array("E South America Standard Time" , "(GMT-03:00) Brasilia"), - "070" => array("SA Eastern Standard Time", "(GMT-03:00) Buenos Aires, Georgetown"), - "073" => array("Greenland Standard Time", "(GMT-03:00) Greenland"), - "075" => array("Mid-Atlantic Standard Time", "(GMT-02:00) Mid-Atlantic"), - "080" => array("Azores Standard Time", "(GMT-01:00) Azores"), - "083" => array("Cape Verde Standard Time", "(GMT-01:00) Cape Verde Islands"), - "085" => array("GMT Standard Time", "(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London"), - "090" => array("Greenwich Standard Time", "(GMT) Casablanca, Monrovia"), - "095" => array("Central Europe Standard Time", "(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague"), - "100" => array("Central European Standard Time", "(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb"), - "105" => array("Romance Standard Time", "(GMT+01:00) Brussels, Copenhagen, Madrid, Paris"), - "110" => array("W Europe Standard Time", "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"), - "113" => array("W Central Africa Standard Time", "(GMT+01:00) West Central Africa"), - "115" => array("E Europe Standard Time", "(GMT+02:00) Bucharest"), - "120" => array("Egypt Standard Time", "(GMT+02:00) Cairo"), - "125" => array("FLE Standard Time", "(GMT+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius"), - "130" => array("GTB Standard Time", "(GMT+02:00) Athens, Istanbul, Minsk"), - "135" => array("Israel Standard Time", "(GMT+02:00) Jerusalem"), - "140" => array("South Africa Standard Time", "(GMT+02:00) Harare, Pretoria"), - "145" => array("Russian Standard Time", "(GMT+03:00) Moscow, St. Petersburg, Volgograd"), - "150" => array("Arab Standard Time", "(GMT+03:00) Kuwait, Riyadh"), - "155" => array("E Africa Standard Time", "(GMT+03:00) Nairobi"), - "158" => array("Arabic Standard Time", "(GMT+03:00) Baghdad"), - "160" => array("Iran Standard Time", "(GMT+03:30) Tehran"), - "165" => array("Arabian Standard Time", "(GMT+04:00) Abu Dhabi, Muscat"), - "170" => array("Caucasus Standard Time", "(GMT+04:00) Baku, Tbilisi, Yerevan"), - "175" => array("Transitional Islamic State of Afghanistan Standard Time","(GMT+04:30) Kabul"), - "180" => array("Ekaterinburg Standard Time", "(GMT+05:00) Ekaterinburg"), - "185" => array("West Asia Standard Time", "(GMT+05:00) Islamabad, Karachi, Tashkent"), - "190" => array("India Standard Time", "(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi"), - "193" => array("Nepal Standard Time", "(GMT+05:45) Kathmandu"), - "195" => array("Central Asia Standard Time", "(GMT+06:00) Astana, Dhaka"), - "200" => array("Sri Lanka Standard Time", "(GMT+06:00) Sri Jayawardenepura"), - "201" => array("N Central Asia Standard Time", "(GMT+06:00) Almaty, Novosibirsk"), - "203" => array("Myanmar Standard Time", "(GMT+06:30) Yangon Rangoon"), - "205" => array("SE Asia Standard Time", "(GMT+07:00) Bangkok, Hanoi, Jakarta"), - "207" => array("North Asia Standard Time", "(GMT+07:00) Krasnoyarsk"), - "210" => array("China Standard Time", "(GMT+08:00) Beijing, Chongqing, Hong Kong SAR, Urumqi"), - "215" => array("Singapore Standard Time", "(GMT+08:00) Kuala Lumpur, Singapore"), - "220" => array("Taipei Standard Time", "(GMT+08:00) Taipei"), - "225" => array("W Australia Standard Time", "(GMT+08:00) Perth"), - "227" => array("North Asia East Standard Time", "(GMT+08:00) Irkutsk, Ulaanbaatar"), - "230" => array("Korea Standard Time", "(GMT+09:00) Seoul"), - "235" => array("Tokyo Standard Time", "(GMT+09:00) Osaka, Sapporo, Tokyo"), - "240" => array("Yakutsk Standard Time", "(GMT+09:00) Yakutsk"), - "245" => array("AUS Central Standard Time", "(GMT+09:30) Darwin"), - "250" => array("Cen Australia Standard Time", "(GMT+09:30) Adelaide"), - "255" => array("AUS Eastern Standard Time", "(GMT+10:00) Canberra, Melbourne, Sydney"), - "260" => array("E Australia Standard Time", "(GMT+10:00) Brisbane"), - "265" => array("Tasmania Standard Time", "(GMT+10:00) Hobart"), - "270" => array("Vladivostok Standard Time", "(GMT+10:00) Vladivostok"), - "275" => array("West Pacific Standard Time", "(GMT+10:00) Guam, Port Moresby"), - "280" => array("Central Pacific Standard Time", "(GMT+11:00) Magadan, Solomon Islands, New Caledonia"), - "285" => array("Fiji Islands Standard Time", "(GMT+12:00) Fiji Islands, Kamchatka, Marshall Islands"), - "290" => array("New Zealand Standard Time", "(GMT+12:00) Auckland, Wellington"), - "300" => array("Tonga Standard Time", "(GMT+13:00) Nuku'alofa"), - ); - - /** - * Python generated offset list - * dots in keys were removed - * - * Array indices - * 0 = lBias - * 1 = lStandardBias - * 2 = lDSTBias - * 3 = wStartYear - * 4 = wStartMonth - * 5 = wStartDOW - * 6 = wStartDay - * 7 = wStartHour - * 8 = wStartMinute - * 9 = wStartSecond - * 10 = wStartMilliseconds - * 11 = wEndYear - * 12 = wEndMonth - * 13 = wEndDOW - * 14 = wEndDay - * 15 = wEndHour - * 16 = wEndMinute - * 17 = wEndSecond - * 18 = wEndMilloseconds - * - * As the $tzoneoffsets and the $mstzones need to be resolved in both directions, - * some offsets are commented as they are not available in the $mstzones. - * - * Created at: 01.06.2012 - */ - private static $tzonesoffsets = array( - "Transitional Islamic State of Afghanistan Standard Time" - => array(-270, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Alaskan Standard Time" => array(540, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "Arab Standard Time" => array(-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Arabian Standard Time" => array(-240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Arabic Standard Time" => array(-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Argentina Standard Time" => array(180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Atlantic Standard Time" => array(240, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "AUS Central Standard Time" => array(-570, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "AUS Eastern Standard Time" => array(-600, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0), - //"Azerbaijan Standard Time" => array(-240, 0, -60, 0, 10, 0, 5, 5, 0, 0, 0, 0, 3, 0, 5, 4, 0, 0, 0), - "Azores Standard Time" => array(60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - //"Bangladesh Standard Time" => array(-360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Canada Central Standard Time" => array(360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Cape Verde Standard Time" => array(60, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Caucasus Standard Time" => array(-240, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Cen Australia Standard Time" => array(-570, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0), - "Central America Standard Time" => array(360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Central Asia Standard Time" => array(-360, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Central Brazilian Standard Time" => array(240, 0, -60, 0, 2, 6, 4, 23, 59, 59, 999, 0, 10, 6, 3, 23, 59, 59, 999), - "Central Europe Standard Time" => array(-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Central European Standard Time" => array(-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Central Pacific Standard Time" => array(-660, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Central Standard Time" => array(360, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "Mexico Standard Time" => array(360, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 4, 0, 1, 2, 0, 0, 0), - "China Standard Time" => array(-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Dateline Standard Time" => array(720, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "E Africa Standard Time" => array(-180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "E Australia Standard Time" => array(-600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "E Europe Standard Time" => array(-120, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "E South America Standard Time" => array(180, 0, -60, 0, 2, 6, 4, 23, 59, 59, 999, 0, 10, 6, 3, 23, 59, 59, 999), - "Eastern Standard Time" => array(300, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "Egypt Standard Time" => array(-120, 0, -60, 0, 9, 4, 5, 23, 59, 59, 999, 0, 4, 4, 5, 23, 59, 59, 999), - "Ekaterinburg Standard Time" => array(-300, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Fiji Islands Standard Time" => array(-720, 0, -60, 0, 3, 0, 5, 3, 0, 0, 0, 0, 10, 0, 4, 2, 0, 0, 0), - "FLE Standard Time" => array(-120, 0, -60, 0, 10, 0, 5, 4, 0, 0, 0, 0, 3, 0, 5, 3, 0, 0, 0), - //"Georgian Standard Time" => array(-240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "GMT Standard Time" => array(0, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 3, 0, 5, 1, 0, 0, 0), - "Greenland Standard Time" => array(180, 0, -60, 0, 10, 6, 5, 23, 0, 0, 0, 0, 3, 6, 4, 22, 0, 0, 0), - "Greenwich Standard Time" => array(0, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "GTB Standard Time" => array(-120, 0, -60, 0, 10, 0, 5, 4, 0, 0, 0, 0, 3, 0, 5, 3, 0, 0, 0), - "Hawaiian Standard Time" => array(600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "India Standard Time" => array(-330, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Iran Standard Time" => array(-210, 0, -60, 0, 9, 1, 3, 23, 59, 59, 999, 0, 3, 6, 3, 23, 59, 59, 999), - "Israel Standard Time" => array(-120, 0, -60, 0, 9, 0, 4, 2, 0, 0, 0, 0, 3, 5, 5, 2, 0, 0, 0), - //"Jordan Standard Time" => array(-120, 0, -60, 0, 10, 5, 5, 1, 0, 0, 0, 0, 3, 4, 5, 23, 59, 59, 999), - //"Kamchatka Standard Time" => array(-720, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Korea Standard Time" => array(-540, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Magadan Standard Time" => array(-660, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - //"Mauritius Standard Time" => array(-240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Mid-Atlantic Standard Time" => array(120, 0, -60, 0, 9, 0, 5, 2, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - //"Middle East Standard Time" => array(-120, 0, -60, 0, 10, 6, 5, 23, 59, 59, 999, 0, 3, 6, 4, 23, 59, 59, 999), - //"Montevideo Standard Time" => array(180, 0, -60, 0, 3, 0, 2, 2, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0), - //"Morocco Standard Time" => array(0, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Mountain Standard Time" => array(420, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "Mexico Standard Time 2" => array(420, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 4, 0, 1, 2, 0, 0, 0), - "Myanmar Standard Time" => array(-390, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "N Central Asia Standard Time" => array(-360, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - //"Namibia Standard Time" => array(-60, 0, -60, 0, 4, 0, 1, 2, 0, 0, 0, 0, 9, 0, 1, 2, 0, 0, 0), - "Nepal Standard Time" => array(-345, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "New Zealand Standard Time" => array(-720, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 9, 0, 5, 2, 0, 0, 0), - "Newfoundland and Labrador Standard Time" => array(210, 0, -60, 0, 11, 0, 1, 0, 1, 0, 0, 0, 3, 0, 2, 0, 1, 0, 0), - "North Asia East Standard Time" => array(-480, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "North Asia Standard Time" => array(-420, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Pacific SA Standard Time" => array(240, 0, -60, 0, 3, 6, 2, 23, 59, 59, 999, 0, 10, 6, 2, 23, 59, 59, 999), - "Pacific Standard Time" => array(480, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - //"Pacific Standard Time (Mexico)" => array(480, 0, -60, 0, 10, 0, 5, 2, 0, 0, 0, 0, 4, 0, 1, 2, 0, 0, 0), - //"Pakistan Standard Time" => array(-300, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Paraguay Standard Time" => array(240, 0, -60, 0, 4, 6, 1, 23, 59, 59, 999, 0, 10, 6, 1, 23, 59, 59, 999), - "Romance Standard Time" => array(-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "Russian Standard Time" => array(-180, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "SA Eastern Standard Time" => array(180, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "SA Pacific Standard Time" => array(300, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "SA Western Standard Time" => array(240, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Samoa Standard Time" => array(660, 0, -60, 0, 3, 6, 5, 23, 59, 59, 999, 0, 9, 6, 5, 23, 59, 59, 999), - "SE Asia Standard Time" => array(-420, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Singapore Standard Time" => array(-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "South Africa Standard Time" => array(-120, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Sri Lanka Standard Time" => array(-330, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Syria Standard Time" => array(-120, 0, -60, 0, 10, 4, 5, 23, 59, 59, 999, 0, 4, 4, 1, 23, 59, 59, 999), - "Taipei Standard Time" => array(-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Tasmania Standard Time" => array(-600, 0, -60, 0, 4, 0, 1, 3, 0, 0, 0, 0, 10, 0, 1, 2, 0, 0, 0), - "Tokyo Standard Time" => array(-540, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Tonga Standard Time" => array(-780, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"Ulaanbaatar Standard Time" => array(-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "US Eastern Standard Time" => array(300, 0, -60, 0, 11, 0, 1, 2, 0, 0, 0, 0, 3, 0, 2, 2, 0, 0, 0), - "US Mountain Standard Time" => array(420, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"UTC" => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"UTC+12" => array(-720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"UTC-02" => array(120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - //"UTC-11" => array(660, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Venezuela Standard Time" => array(270, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Vladivostok Standard Time" => array(-600, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "W Australia Standard Time" => array(-480, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "W Central Africa Standard Time" => array(-60, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "W Europe Standard Time" => array(-60, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - "West Asia Standard Time" => array(-300, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "West Pacific Standard Time" => array(-600, 0, -60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - "Yakutsk Standard Time" => array(-540, 0, -60, 0, 10, 0, 5, 3, 0, 0, 0, 0, 3, 0, 5, 2, 0, 0, 0), - ); - - /** - * Generated list of PHP timezones in GMT timezones - * - * Created at: 01.06.2012 - */ - private static $phptimezones = array( - // -720 min - "Dateline Standard Time" => array( - "Etc/GMT+12", - ), - - // -660 min - "Samoa Standard Time" => array( - "Etc/GMT+11", - "Pacific/Midway", - "Pacific/Niue", - "Pacific/Pago_Pago", - "Pacific/Samoa", - "US/Samoa", - ), - - // -600 min - "Hawaiian Standard Time" => array( - "America/Adak", - "America/Atka", - "Etc/GMT+10", - "HST", - "Pacific/Honolulu", - "Pacific/Johnston", - "Pacific/Rarotonga", - "Pacific/Tahiti", - "US/Aleutian", - "US/Hawaii", - ), - - // -570 min - "-570" => array( - "Pacific/Marquesas", - ), - - // -540 min - "Alaskan Standard Time" => array( - "America/Anchorage", - "America/Juneau", - "America/Nome", - "America/Sitka", - "America/Yakutat", - "Etc/GMT+9", - "Pacific/Gambier", - "US/Alaska", - ), - - // -480 min - "Pacific Standard Time" => array( - "America/Dawson", - "America/Ensenada", - "America/Los_Angeles", - "America/Metlakatla", - "America/Santa_Isabel", - "America/Tijuana", - "America/Vancouver", - "America/Whitehorse", - "Canada/Pacific", - "Canada/Yukon", - "Etc/GMT+8", - "Mexico/BajaNorte", - "Pacific/Pitcairn", - "PST8PDT", - "US/Pacific", - "US/Pacific-New", - ), - - // -420 min - "US Mountain Standard Time" => array( - "America/Boise", - "America/Cambridge_Bay", - "America/Chihuahua", - "America/Creston", - "America/Dawson_Creek", - "America/Denver", - "America/Edmonton", - "America/Hermosillo", - "America/Inuvik", - "America/Mazatlan", - "America/Ojinaga", - "America/Phoenix", - "America/Shiprock", - "America/Yellowknife", - "Canada/Mountain", - "Etc/GMT+7", - "Mexico/BajaSur", - "MST", - "MST7MDT", - "Navajo", - "US/Arizona", - "US/Mountain", - ), - - // -360 min - "Central Standard Time" => array( - "America/Chicago", - "America/Indiana/Knox", - "America/Indiana/Tell_City", - "America/Knox_IN", - "America/North_Dakota/Beulah", - "America/North_Dakota/Center", - "America/North_Dakota/New_Salem", - "America/Rainy_River", - "America/Rankin_Inlet", - "America/Regina", - "America/Resolute", - "America/Swift_Current", - "America/Tegucigalpa", - "America/Winnipeg", - "US/Central", - "US/Indiana-Starke", - "CST6CDT", - "Etc/GMT+6", - ), - "Canada Central Standard Time" => array( - "Canada/Central", - "Canada/East-Saskatchewan", - "Canada/Saskatchewan", - ), - "Mexico Standard Time" => array( - "America/Mexico_City", - "America/Monterrey", - "Mexico/General", - ), - "Central America Standard Time" => array( - "America/Bahia_Banderas", - "America/Belize", - "America/Cancun", - "America/Costa_Rica", - "America/El_Salvador", - "America/Guatemala", - "America/Managua", - "America/Matamoros", - "America/Menominee", - "America/Merida", - "Chile/EasterIsland", - "Pacific/Easter", - "Pacific/Galapagos", - ), - - // -300 min - "US Eastern Standard Time" => array( - "America/Detroit", - "America/Fort_Wayne", - "America/Grand_Turk", - "America/Indiana/Indianapolis", - "America/Indiana/Marengo", - "America/Indiana/Petersburg", - "America/Indiana/Vevay", - "America/Indiana/Vincennes", - "America/Indiana/Winamac", - "America/Indianapolis", - "America/Jamaica", - "America/Kentucky/Louisville", - "America/Kentucky/Monticello", - "America/Louisville", - "America/Montreal", - "America/New_York", - "America/Thunder_Bay", - "America/Toronto", - "Canada/Eastern", - "Cuba", - "EST", - "EST5EDT", - "Etc/GMT+5", - "Jamaica", - "US/East-Indiana", - "US/Eastern", - "US/Michigan", - ), - "SA Pacific Standard Time" => array( - "America/Atikokan", - "America/Bogota", - "America/Cayman", - "America/Coral_Harbour", - "America/Guayaquil", - "America/Havana", - "America/Iqaluit", - "America/Lima", - "America/Nassau", - "America/Nipigon", - "America/Panama", - "America/Pangnirtung", - "America/Port-au-Prince", - ), - - // -270 min - "Venezuela Standard Time" => array( - "America/Caracas", - ), - // -240 min - "Atlantic Standard Time" => array( - "America/Barbados", - "America/Blanc-Sablon", - "America/Glace_Bay", - "America/Goose_Bay", - "America/Halifax", - "America/Lower_Princes", - "America/St_Barthelemy", - "America/St_Kitts", - "America/St_Lucia", - "America/St_Thomas", - "America/St_Vincent", - "America/Thule", - "America/Tortola", - "America/Virgin", - "Atlantic/Bermuda", - "Canada/Atlantic", - "Etc/GMT+4", - ), - "SA Western Standard Time" => array( - "America/Anguilla", - "America/Antigua", - "America/Aruba", - "America/Asuncion", - "America/Boa_Vista", - "America/Campo_Grande", - "America/Cuiaba", - "America/Curacao", - "America/Dominica", - "America/Eirunepe", - "America/Grenada", - "America/Guadeloupe", - "America/Guyana", - "America/Kralendijk", - "America/La_Paz", - "America/Manaus", - "America/Marigot", - "America/Martinique", - "America/Moncton", - "America/Montserrat", - "America/Port_of_Spain", - "America/Porto_Acre", - "America/Porto_Velho", - "America/Puerto_Rico", - "America/Rio_Branco", - "Brazil/Acre", - "Brazil/West", - ), - "Pacific SA Standard Time" => array( - "America/Santiago", - "America/Santo_Domingo", - "Antarctica/Palmer", - "Chile/Continental", - ), - - // -210 min - "Newfoundland and Labrador Standard Time" => array( - "America/St_Johns", - "Canada/Newfoundland", - ), - - // -180 min - "E South America Standard Time" => array( - "America/Araguaina", - "America/Bahia", - "America/Belem", - "America/Fortaleza", - "America/Maceio", - "America/Recife", - "America/Sao_Paulo", - "Brazil/East", - "Etc/GMT+3", - ), - "SA Eastern Standard Time" => array( - "America/Argentina/Buenos_Aires", - "America/Argentina/Catamarca", - "America/Argentina/ComodRivadavia", - "America/Argentina/Cordoba", - "America/Argentina/Jujuy", - "America/Argentina/La_Rioja", - "America/Argentina/Mendoza", - "America/Argentina/Rio_Gallegos", - "America/Argentina/Salta", - "America/Argentina/San_Juan", - "America/Argentina/San_Luis", - "America/Argentina/Tucuman", - "America/Argentina/Ushuaia", - "America/Buenos_Aires", - "America/Catamarca", - "America/Cayenne", - "America/Cordoba", - "America/Godthab", - "America/Jujuy", - "America/Mendoza", - "America/Miquelon", - "America/Montevideo", - "America/Paramaribo", - "America/Rosario", - "America/Santarem", - ), - "Greenland Standard Time" => array( - "Antarctica/Rothera", - "Atlantic/Stanley", - ), - - // -120 min - "Mid-Atlantic Standard Time" => array( - "America/Noronha", - "Atlantic/South_Georgia", - "Brazil/DeNoronha", - "Etc/GMT+2", - ), - - // -60 min - "Azores Standard Time" => array( - "Atlantic/Azores", - "Etc/GMT+1", - ), - "Cape Verde Standard Time" => array( - "America/Scoresbysund", - "Atlantic/Cape_Verde", - ), - - // 0 min - "GMT Standard Time" => array( - "Eire", - "Etc/GMT", - "Etc/GMT+0", - "Etc/GMT-0", - "Etc/GMT0", - "Etc/Greenwich", - "Etc/UCT", - "Etc/Universal", - "Etc/UTC", - "Etc/Zulu", - "Europe/Belfast", - "Europe/Dublin", - "Europe/Guernsey", - "Europe/Isle_of_Man", - "Europe/Jersey", - "Europe/Lisbon", - "Europe/London", - "Factory", - "GB", - "GB-Eire", - "GMT", - "GMT+0", - "GMT-0", - "GMT0", - "Greenwich", - "Iceland", - "Portugal", - "UCT", - "Universal", - "UTC", - ), - "Greenwich Standard Time" => array( - "Africa/Abidjan", - "Africa/Accra", - "Africa/Bamako", - "Africa/Banjul", - "Africa/Bissau", - "Africa/Casablanca", - "Africa/Conakry", - "Africa/Dakar", - "Africa/El_Aaiun", - "Africa/Freetown", - "Africa/Lome", - "Africa/Monrovia", - "Africa/Nouakchott", - "Africa/Ouagadougou", - "Africa/Sao_Tome", - "Africa/Timbuktu", - "America/Danmarkshavn", - "Atlantic/Canary", - "Atlantic/Faeroe", - "Atlantic/Faroe", - "Atlantic/Madeira", - "Atlantic/Reykjavik", - "Atlantic/St_Helena", - "Zulu", - ), - - // +60 min - "Central Europe Standard Time" => array( - "Europe/Belgrade", - "Europe/Bratislava", - "Europe/Budapest", - "Europe/Ljubljana", - "Europe/Prague", - "Europe/Vaduz", - ), - "Central European Standard Time" => array( - "Europe/Sarajevo", - "Europe/Skopje", - "Europe/Warsaw", - "Europe/Zagreb", - "MET", - "Poland", - ), - "Romance Standard Time" => array( - "Europe/Andorra", - "Europe/Brussels", - "Europe/Copenhagen", - "Europe/Gibraltar", - "Europe/Madrid", - "Europe/Malta", - "Europe/Monaco", - "Europe/Paris", - "Europe/Podgorica", - "Europe/San_Marino", - "Europe/Tirane", - ), - "W Europe Standard Time" => array( - "Europe/Amsterdam", - "Europe/Berlin", - "Europe/Luxembourg", - "Europe/Vatican", - "Europe/Rome", - "Europe/Stockholm", - "Arctic/Longyearbyen", - "Europe/Vienna", - "Europe/Zurich", - "Europe/Oslo", - "WET", - "CET", - "Etc/GMT-1", - ), - "W Central Africa Standard Time" => array( - "Africa/Algiers", - "Africa/Bangui", - "Africa/Brazzaville", - "Africa/Ceuta", - "Africa/Douala", - "Africa/Kinshasa", - "Africa/Lagos", - "Africa/Libreville", - "Africa/Luanda", - "Africa/Malabo", - "Africa/Ndjamena", - "Africa/Niamey", - "Africa/Porto-Novo", - "Africa/Tunis", - "Africa/Windhoek", - "Atlantic/Jan_Mayen", - ), - - // +120 min - "E Europe Standard Time" => array( - "Europe/Bucharest", - "EET", - "Etc/GMT-2", - "Europe/Chisinau", - "Europe/Mariehamn", - "Europe/Nicosia", - "Europe/Simferopol", - "Europe/Tiraspol", - "Europe/Uzhgorod", - "Europe/Zaporozhye", - ), - "Egypt Standard Time" => array( - "Africa/Cairo", - "Africa/Tripoli", - "Egypt", - "Libya", - ), - "FLE Standard Time" => array( - "Europe/Helsinki", - "Europe/Kiev", - "Europe/Riga", - "Europe/Sofia", - "Europe/Tallinn", - "Europe/Vilnius", - ), - "GTB Standard Time" => array( - "Asia/Istanbul", - "Europe/Athens", - "Europe/Istanbul", - "Turkey", - ), - "Israel Standard Time" => array( - "Asia/Amman", - "Asia/Beirut", - "Asia/Damascus", - "Asia/Gaza", - "Asia/Hebron", - "Asia/Nicosia", - "Asia/Tel_Aviv", - "Asia/Jerusalem", - "Israel", - ), - "South Africa Standard Time" => array( - "Africa/Blantyre", - "Africa/Bujumbura", - "Africa/Gaborone", - "Africa/Harare", - "Africa/Johannesburg", - "Africa/Kigali", - "Africa/Lubumbashi", - "Africa/Lusaka", - "Africa/Maputo", - "Africa/Maseru", - "Africa/Mbabane", - ), - - // +180 min - "Russian Standard Time" => array( - "Antarctica/Syowa", - "Europe/Kaliningrad", - "Europe/Minsk", - "Etc/GMT-3", - ), - "Arab Standard Time" => array( - "Asia/Qatar", - "Asia/Kuwait", - "Asia/Riyadh", - ), - "E Africa Standard Time" => array( - "Africa/Addis_Ababa", - "Africa/Asmara", - "Africa/Asmera", - "Africa/Dar_es_Salaam", - "Africa/Djibouti", - "Africa/Juba", - "Africa/Kampala", - "Africa/Khartoum", - "Africa/Mogadishu", - "Africa/Nairobi", - ), - "Arabic Standard Time" => array( - "Asia/Aden", - "Asia/Baghdad", - "Asia/Bahrain", - "Indian/Antananarivo", - "Indian/Comoro", - "Indian/Mayotte", - ), - - // +210 min - "Iran Standard Time" => array( - "Asia/Tehran", - "Iran", - ), - - // +240 min - "Arabian Standard Time" => array( - "Asia/Dubai", - "Asia/Muscat", - "Indian/Mahe", - "Indian/Mauritius", - "Indian/Reunion", - ), - "Caucasus Standard Time" => array( - "Asia/Baku", - "Asia/Tbilisi", - "Asia/Yerevan", - "Etc/GMT-4", - "Europe/Moscow", - "Europe/Samara", - "Europe/Volgograd", - "W-SU", - ), - - // +270 min - "Transitional Islamic State of Afghanistan Standard Time" => array( - "Asia/Kabul", - ), - - // +300 min - "Ekaterinburg Standard Time" => array( - "Antarctica/Mawson", - ), - "West Asia Standard Time" => array( - "Asia/Aqtau", - "Asia/Aqtobe", - "Asia/Ashgabat", - "Asia/Ashkhabad", - "Asia/Dushanbe", - "Asia/Karachi", - "Asia/Oral", - "Asia/Samarkand", - "Asia/Tashkent", - "Etc/GMT-5", - "Indian/Kerguelen", - "Indian/Maldives", - ), - - // +330 min - "India Standard Time" => array( - "Asia/Calcutta", - "Asia/Colombo", - "Asia/Kolkata", - ), - - // +345 min - "Nepal Standard Time" => array( - "Asia/Kathmandu", - "Asia/Katmandu", - ), - - // +360 min - "Central Asia Standard Time" => array( - "Asia/Dacca", - "Asia/Dhaka", - ), - "Sri Lanka Standard Time" => array( - "Indian/Chagos", - ), - "N Central Asia Standard Time" => array( - "Antarctica/Vostok", - "Asia/Almaty", - "Asia/Bishkek", - "Asia/Qyzylorda", - "Asia/Thimbu", - "Asia/Thimphu", - "Asia/Yekaterinburg", - "Etc/GMT-6", - ), - - // +390 min - "Myanmar Standard Time" => array( - "Asia/Rangoon", - "Indian/Cocos", - ), - - // +420 min - "SE Asia Standard Time" => array( - "Asia/Bangkok", - "Asia/Ho_Chi_Minh", - "Asia/Hovd", - "Asia/Jakarta", - "Asia/Phnom_Penh", - "Asia/Saigon", - "Indian/Christmas", - ), - "North Asia Standard Time" => array( - "Antarctica/Davis", - "Asia/Novokuznetsk", - "Asia/Novosibirsk", - "Asia/Omsk", - "Asia/Pontianak", - "Asia/Vientiane", - "Etc/GMT-7", - ), - - // +480 min - "China Standard Time" => array( - "Asia/Brunei", - "Asia/Choibalsan", - "Asia/Chongqing", - "Asia/Chungking", - "Asia/Harbin", - "Asia/Hong_Kong", - "Asia/Shanghai", - "Asia/Ujung_Pandang", - "Asia/Urumqi", - "Hongkong", - "PRC", - "ROC", - ), - "Singapore Standard Time" => array( - "Singapore", - "Asia/Singapore", - "Asia/Kuala_Lumpur", - ), - "Taipei Standard Time" => array( - "Asia/Taipei", - ), - "W Australia Standard Time" => array( - "Australia/Perth", - "Australia/West", - ), - "North Asia East Standard Time" => array( - "Antarctica/Casey", - "Asia/Kashgar", - "Asia/Krasnoyarsk", - "Asia/Kuching", - "Asia/Macao", - "Asia/Macau", - "Asia/Makassar", - "Asia/Manila", - "Etc/GMT-8", - "Asia/Ulaanbaatar", - "Asia/Ulan_Bator", - ), - - // +525 min - "525" => array( - "Australia/Eucla", - ), - - // +540 min - "Korea Standard Time" => array( - "Asia/Seoul", - "Asia/Pyongyang", - "ROK", - ), - "Tokyo Standard Time" => array( - "Asia/Tokyo", - "Japan", - "Etc/GMT-9", - ), - "Yakutsk Standard Time" => array( - "Asia/Dili", - "Asia/Irkutsk", - "Asia/Jayapura", - "Pacific/Palau", - ), - - // +570 min - "AUS Central Standard Time" => array( - "Australia/Darwin", - "Australia/North", - ), - // DST - "Cen Australia Standard Time" => array( - "Australia/Adelaide", - "Australia/Broken_Hill", - "Australia/South", - "Australia/Yancowinna", - ), - - // +600 min - "AUS Eastern Standard Time" => array( - "Australia/Canberra", - "Australia/Melbourne", - "Australia/Sydney", - "Australia/Currie", - "Australia/ACT", - "Australia/NSW", - "Australia/Victoria", - ), - "E Australia Standard Time" => array( - "Etc/GMT-10", - "Australia/Brisbane", - "Australia/Queensland", - "Australia/Lindeman", - ), - "Tasmania Standard Time" => array( - "Australia/Hobart", - "Australia/Tasmania", - ), - "Vladivostok Standard Time" => array( - "Antarctica/DumontDUrville", - ), - "West Pacific Standard Time" => array( - "Asia/Yakutsk", - "Pacific/Chuuk", - "Pacific/Guam", - "Pacific/Port_Moresby", - "Pacific/Saipan", - "Pacific/Truk", - "Pacific/Yap", - ), - - // +630 min - "630" => array( - "Australia/LHI", - "Australia/Lord_Howe", - ), - - // +660 min - "Central Pacific Standard Time" => array( - "Antarctica/Macquarie", - "Asia/Sakhalin", - "Asia/Vladivostok", - "Etc/GMT-11", - "Pacific/Efate", - "Pacific/Guadalcanal", - "Pacific/Kosrae", - "Pacific/Noumea", - "Pacific/Pohnpei", - "Pacific/Ponape", - ), - - // 690 min - "690" => array( - "Pacific/Norfolk", - ), - - // +720 min - "Fiji Islands Standard Time" => array( - "Asia/Anadyr", - "Asia/Kamchatka", - "Asia/Magadan", - "Kwajalein", - ), - "New Zealand Standard Time" => array( - "Antarctica/McMurdo", - "Antarctica/South_Pole", - "Etc/GMT-12", - "NZ", - "Pacific/Auckland", - "Pacific/Fiji", - "Pacific/Funafuti", - "Pacific/Kwajalein", - "Pacific/Majuro", - "Pacific/Nauru", - "Pacific/Tarawa", - "Pacific/Wake", - "Pacific/Wallis", - ), - - // +765 min - "765" => array( - "NZ-CHAT", - "Pacific/Chatham", - ), - - // +780 min - "Tonga Standard Time" => array( - "Etc/GMT-13", - "Pacific/Apia", - "Pacific/Enderbury", - "Pacific/Tongatapu", - ), - - // +840 min - "840" => array( - "Etc/GMT-14", - "Pacific/Fakaofo", - "Pacific/Kiritimati", - ), - ); - - /** - * Returns a full timezone array - * - * @param string $phptimezone (opt) a php timezone string. - * If omitted the env. default timezone is used. - * - * @access public - * @return array - */ - static public function GetFullTZ($phptimezone = false) { - if ($phptimezone === false) - $phptimezone = date_default_timezone_get(); - - ZLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::GetFullTZ() for ". $phptimezone); - - $servertzname = self::guessTZNameFromPHPName($phptimezone); - return self::GetFullTZFromTZName($servertzname); - } - - /** - * Returns a full timezone array - * - * @param string $tzname a TZID value - * - * @access public - * @return array - */ - static public function GetFullTZFromTZName($tzname) { - if (!array_key_exists($tzname, self::$tzonesoffsets)) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("TimezoneUtil::GetFullTZFromTZName('%s'): Is a PHP TimeZone, converting", $tzname)); - $tzname = self::guessTZNameFromPHPName($tzname); - } - - $offset = self::$tzonesoffsets[$tzname]; - - $tz = array( - "bias" => $offset[0], - "tzname" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)), - "dstendyear" => $offset[3], - "dstendmonth" => $offset[4], - "dstendday" => $offset[5], - "dstendweek" => $offset[6], - "dstendhour" => $offset[7], - "dstendminute" => $offset[8], - "dstendsecond" => $offset[9], - "dstendmillis" => $offset[10], - "stdbias" => $offset[1], - "tznamedst" => self::encodeTZName(self::getMSTZnameFromTZName($tzname)), - "dststartyear" => $offset[11], - "dststartmonth" => $offset[12], - "dststartday" => $offset[13], - "dststartweek" => $offset[14], - "dststarthour" => $offset[15], - "dststartminute" => $offset[16], - "dststartsecond" => $offset[17], - "dststartmillis" => $offset[18], - "dstbias" => $offset[2] - ); - - return $tz; - } - - /** - * Sets the timezone name by matching data from the offset (bias etc) - * - * @param array $offset a z-push timezone array - * - * @access public - * @return array - */ - static public function FillTZNames($tz) { - ZLog::Write(LOGLEVEL_DEBUG, "TimezoneUtil::FillTZNames() filling up bias ". $tz["bias"]); - if (!isset($tz["bias"])) - ZLog::Write(LOGLEVEL_WARN, "TimezoneUtil::FillTZNames() submitted TZ array does not have a bias"); - else { - $tzname = self::guessTZNameFromOffset($tz); - $tz['tzname'] = $tz['tznamedst'] = self::encodeTZName(self::getMSTZnameFromTZName($tzname)); - } - return $tz; - } - - /** - * Tries to find a timezone using the Bias and other offset parameters - * - * @param array $offset a z-push timezone array - * - * @access public - * @return string - */ - static private function guessTZNameFromOffset($offset) { - // try to find a quite exact match - foreach (self::$tzonesoffsets as $tzname => $tzoffset) { - if ($offset["bias"] == $tzoffset[0] && - isset($offset["dstendmonth"]) && $offset["dstendmonth"] == $tzoffset[4] && - isset($offset["dstendday"]) && $offset["dstendday"] == $tzoffset[6] && - isset($offset["dststartmonth"]) && $offset["dststartmonth"] == $tzoffset[12] && - isset($offset["dststartday"]) && $offset["dststartday"] == $tzoffset[14]) - return $tzname; - } - - // try to find a bias match - foreach (self::$tzonesoffsets as $tzname => $tzoffset) { - if ($offset["bias"] == $tzoffset[0]) - return $tzname; - } - - // nothing found? return gmt - ZLog::Write(LOGLEVEL_WARN, "TimezoneUtil::guessTZNameFromOffset() no timezone found for the data submitted. Returning 'GMT Standard Time'."); - return "GMT Standard Time"; - } - - /** - * Tries to find a AS timezone for a php timezone - * - * @param string $phpname a php timezone name - * - * @access public - * @return string - */ - static private function guessTZNameFromPHPName($phpname) { - foreach (self::$phptimezones as $tzn => $phptzs) { - if (in_array($phpname, $phptzs)) { - $tzname = $tzn; - break; - } - } - - if (!isset($tzname) || is_int($tzname)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("TimezoneUtil::guessTZNameFromPHPName() no compatible timezone found for '%s'. Returning 'GMT Standard Time'. Please contact the Z-Push dev team.", $phpname)); - return self::$mstzones["085"][0]; - } - - return $tzname; - } - - /** - * Returns an AS compatible tz name - * - * @param string $name internal timezone name - * - * @access public - * @return string - */ - static public function getMSTZnameFromTZName($name) { - foreach (self::$mstzones as $mskey => $msdefs) { - if ($name == $msdefs[0]) { - return $msdefs[1]; - } - } - - // Not found? Then retrieve the correct TZName first and try again. - // That's ugly and needs a proper fix. But for now this method can convert - // - Europe/Berlin - // - W Europe Standard Time - // to "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna" - // which is more correct than the hardcoded default of (GMT+00:00...) - $tzName = ''; - foreach (self::$phptimezones as $tzn => $phptzs) { - if (in_array($name, $phptzs)) { - $tzName = $tzn; - break; - } - } - if ($tzName != '') { - foreach (self::$mstzones as $mskey => $msdefs) { - if ($tzName == $msdefs[0]) { - return $msdefs[1]; - } - } - } - - ZLog::Write(LOGLEVEL_WARN, sprintf("TimezoneUtil::getMSTZnameFromTZName() no MS name found for '%s'. Returning '(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London'", $name)); - return self::$mstzones["085"][1]; - } - - /** - * Encodes the tz name to UTF-16 compatible with a syncblob - * - * @param string $name timezone name - * - * @access public - * @return string - */ - static private function encodeTZName($name) { - return substr(iconv('UTF-8', 'UTF-16', $name),2,-1); - } - - /** - * Test to check if $mstzones and $tzonesoffsets can be resolved - * in both directions. - * - * @access public - * @return - */ - static public function TZtest() { - foreach (self::$mstzones as $mskey => $msdefs) { - if (!array_key_exists($msdefs[0], self::$tzonesoffsets)) - echo "key '". $msdefs[0]. "' not found in tzonesoffsets\n"; - } - - foreach (self::$tzonesoffsets as $tzname => $offset) { - $found = false; - foreach (self::$mstzones as $mskey => $msdefs) { - if ($tzname == $msdefs[0]) { - $found = true; - break; - } - } - if (!$found) - echo "key '$tzname' NOT FOUND\n"; - } - } - - /** - * Pack timezone info for Sync - * - * @param array $tz - * - * @access private - * @return string - */ - static public function GetSyncBlobFromTZ($tz) { - // set the correct TZ name (done using the Bias) - if (!isset($tz["tzname"]) || !$tz["tzname"] || !isset($tz["tznamedst"]) || !$tz["tznamedst"]) - $tz = TimezoneUtil::FillTZNames($tz); - - $packed = pack("la64vvvvvvvv" . "la64vvvvvvvv" . "l", - $tz["bias"], $tz["tzname"], 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"], - $tz["stdbias"], $tz["tznamedst"], 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"], - $tz["dstbias"]); - - return $packed; - } -} diff --git a/sources/lib/utils/utils.php b/sources/lib/utils/utils.php deleted file mode 100644 index 0c47871..0000000 --- a/sources/lib/utils/utils.php +++ /dev/null @@ -1,1069 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Utils { - /** - * Prints a variable as string - * If a boolean is sent, 'true' or 'false' is displayed - * - * @param string $var - * @access public - * @return string - */ - static public function PrintAsString($var) { - return ($var)?(($var===true)?'true':$var):(($var===false)?'false':(($var==='')?'empty':$var)); -//return ($var)?(($var===true)?'true':$var):'false'; - } - - /** - * Splits a "domain\user" string into two values - * If the string cotains only the user, domain is returned empty - * - * @param string $domainuser - * - * @access public - * @return array index 0: user 1: domain - */ - static public function SplitDomainUser($domainuser) { - $pos = strrpos($domainuser, '\\'); - if($pos === false){ - $user = $domainuser; - $domain = ''; - } - else{ - $domain = substr($domainuser,0,$pos); - $user = substr($domainuser,$pos+1); - } - return array($user, $domain); - } - - /** - * Build an address string from the components - * - * @param string $street the street - * @param string $zip the zip code - * @param string $city the city - * @param string $state the state - * @param string $country the country - * - * @access public - * @return string the address string or null - */ - static public function BuildAddressString($street, $zip, $city, $state, $country) { - $out = ""; - - if (isset($country) && $street != "") $out = $country; - - $zcs = ""; - if (isset($zip) && $zip != "") $zcs = $zip; - if (isset($city) && $city != "") $zcs .= (($zcs)?" ":"") . $city; - if (isset($state) && $state != "") $zcs .= (($zcs)?" ":"") . $state; - if ($zcs) $out = $zcs . "\r\n" . $out; - - if (isset($street) && $street != "") $out = $street . (($out)?"\r\n\r\n". $out: "") ; - - return ($out)?$out:null; - } - - /** - * Build the fileas string from the components according to the configuration. - * - * @param string $lastname - * @param string $firstname - * @param string $middlename - * @param string $company - * - * @access public - * @return string fileas - */ - static public function BuildFileAs($lastname = "", $firstname = "", $middlename = "", $company = "") { - if (defined('FILEAS_ORDER')) { - $fileas = $lastfirst = $firstlast = ""; - $names = trim ($firstname . " " . $middlename); - $lastname = trim($lastname); - $company = trim($company); - - // lastfirst is "lastname, firstname middlename" - // firstlast is "firstname middlename lastname" - if (strlen($lastname) > 0) { - $lastfirst = $lastname; - if (strlen($names) > 0){ - $lastfirst .= ", $names"; - $firstlast = "$names $lastname"; - } - else { - $firstlast = $lastname; - } - } - elseif (strlen($names) > 0) { - $lastfirst = $firstlast = $names; - } - - // if fileas with a company is selected - // but company is emtpy then it will - // fallback to firstlast or lastfirst - // (depending on which is selected for company) - switch (FILEAS_ORDER) { - case SYNC_FILEAS_COMPANYONLY: - if (strlen($company) > 0) { - $fileas = $company; - } - elseif (strlen($firstlast) > 0) - $fileas = $lastfirst; - break; - case SYNC_FILEAS_COMPANYLAST: - if (strlen($company) > 0) { - $fileas = $company; - if (strlen($lastfirst) > 0) - $fileas .= "($lastfirst)"; - } - elseif (strlen($lastfirst) > 0) - $fileas = $lastfirst; - break; - case SYNC_FILEAS_COMPANYFIRST: - if (strlen($company) > 0) { - $fileas = $company; - if (strlen($firstlast) > 0) { - $fileas .= " ($firstlast)"; - } - } - elseif (strlen($firstlast) > 0) { - $fileas = $firstlast; - } - break; - case SYNC_FILEAS_FIRSTCOMPANY: - if (strlen($firstlast) > 0) { - $fileas = $firstlast; - if (strlen($company) > 0) { - $fileas .= " ($company)"; - } - } - elseif (strlen($company) > 0) { - $fileas = $company; - } - break; - case SYNC_FILEAS_LASTCOMPANY: - if (strlen($lastfirst) > 0) { - $fileas = $lastfirst; - if (strlen($company) > 0) { - $fileas .= " ($company)"; - } - } - elseif (strlen($company) > 0) { - $fileas = $company; - } - break; - case SYNC_FILEAS_LASTFIRST: - if (strlen($lastfirst) > 0) { - $fileas = $lastfirst; - } - break; - default: - $fileas = $firstlast; - break; - } - if (strlen($fileas) == 0) - ZLog::Write(LOGLEVEL_DEBUG, "Fileas is empty."); - return $fileas; - } - ZLog::Write(LOGLEVEL_DEBUG, "FILEAS_ORDER not defined. Add it to your config.php."); - return null; - } - /** - * Checks if the PHP-MAPI extension is available and in a requested version - * - * @param string $version the version to be checked ("6.30.10-18495", parts or build number) - * - * @access public - * @return boolean installed version is superior to the checked strin - */ - static public function CheckMapiExtVersion($version = "") { - // compare build number if requested - if (preg_match('/^\d+$/', $version) && strlen($version) > 3) { - $vs = preg_split('/-/', phpversion("mapi")); - return ($version <= $vs[1]); - } - - if (extension_loaded("mapi")){ - if (version_compare(phpversion("mapi"), $version) == -1){ - return false; - } - } - else - return false; - - return true; - } - - /** - * Parses and returns an ecoded vCal-Uid from an - * OL compatible GlobalObjectID - * - * @param string $olUid an OL compatible GlobalObjectID - * - * @access public - * @return string the vCal-Uid if available in the olUid, else the original olUid as HEX - */ - static public function GetICalUidFromOLUid($olUid){ - //check if "vCal-Uid" is somewhere in outlookid case-insensitive - $icalUid = stristr($olUid, "vCal-Uid"); - if ($icalUid !== false) { - //get the length of the ical id - go back 4 position from where "vCal-Uid" was found - $begin = unpack("V", substr($olUid, strlen($icalUid) * (-1) - 4, 4)); - //remove "vCal-Uid" and packed "1" and use the ical id length - return substr($icalUid, 12, ($begin[1] - 13)); - } - return strtoupper(bin2hex($olUid)); - } - - /** - * Checks the given UID if it is an OL compatible GlobalObjectID - * If not, the given UID is encoded inside the GlobalObjectID - * - * @param string $icalUid an appointment uid as HEX - * - * @access public - * @return string an OL compatible GlobalObjectID - * - */ - static public function GetOLUidFromICalUid($icalUid) { - if (strlen($icalUid) <= 64) { - $len = 13 + strlen($icalUid); - $OLUid = pack("V", $len); - $OLUid .= "vCal-Uid"; - $OLUid .= pack("V", 1); - $OLUid .= $icalUid; - return hex2bin("040000008200E00074C5B7101A82E0080000000000000000000000000000000000000000". bin2hex($OLUid). "00"); - } - else - return hex2bin($icalUid); - } - - /** - * Extracts the basedate of the GlobalObjectID and the RecurStartTime - * - * @param string $goid OL compatible GlobalObjectID - * @param long $recurStartTime - * - * @access public - * @return long basedate - */ - static public function ExtractBaseDate($goid, $recurStartTime) { - $hexbase = substr(bin2hex($goid), 32, 8); - $day = hexdec(substr($hexbase, 6, 2)); - $month = hexdec(substr($hexbase, 4, 2)); - $year = hexdec(substr($hexbase, 0, 4)); - - if ($day && $month && $year) { - $h = $recurStartTime >> 12; - $m = ($recurStartTime - $h * 4096) >> 6; - $s = $recurStartTime - $h * 4096 - $m * 64; - - return gmmktime($h, $m, $s, $month, $day, $year); - } - else - return false; - } - - /** - * Converts SYNC_FILTERTYPE into a timestamp - * - * @param int Filtertype - * - * @access public - * @return long - */ - static public function GetCutOffDate($restrict) { - switch($restrict) { - case SYNC_FILTERTYPE_1DAY: - $back = 60 * 60 * 24; - break; - case SYNC_FILTERTYPE_3DAYS: - $back = 60 * 60 * 24 * 3; - break; - case SYNC_FILTERTYPE_1WEEK: - $back = 60 * 60 * 24 * 7; - break; - case SYNC_FILTERTYPE_2WEEKS: - $back = 60 * 60 * 24 * 14; - break; - case SYNC_FILTERTYPE_1MONTH: - $back = 60 * 60 * 24 * 31; - break; - case SYNC_FILTERTYPE_3MONTHS: - $back = 60 * 60 * 24 * 31 * 3; - break; - case SYNC_FILTERTYPE_6MONTHS: - $back = 60 * 60 * 24 * 31 * 6; - break; - default: - return 0; // unlimited - } - - return time() - $back; - } - - /** - * Converts SYNC_TRUNCATION into bytes - * - * @param int SYNC_TRUNCATION - * - * @return long - */ - static public function GetTruncSize($truncation) { - switch($truncation) { - case SYNC_TRUNCATION_HEADERS: - return 0; - case SYNC_TRUNCATION_512B: - return 512; - case SYNC_TRUNCATION_1K: - return 1024; - case SYNC_TRUNCATION_2K: - return 2*1024; - case SYNC_TRUNCATION_5K: - return 5*1024; - case SYNC_TRUNCATION_10K: - return 10*1024; - case SYNC_TRUNCATION_20K: - return 20*1024; - case SYNC_TRUNCATION_50K: - return 50*1024; - case SYNC_TRUNCATION_100K: - return 100*1024; - case SYNC_TRUNCATION_ALL: - return 1024*1024; // We'll limit to 1MB anyway - default: - return 1024; // Default to 1Kb - } - } - - /** - * Truncate an UTF-8 encoded sting correctly - * - * If it's not possible to truncate properly, an empty string is returned - * - * @param string $string - the string - * @param string $length - position where string should be cut - * @return string truncated string - */ - static public function Utf8_truncate($string, $length) { - // make sure length is always an interger - $length = (int)$length; - - if (strlen($string) <= $length) - return $string; - - while($length >= 0) { - if ((ord($string[$length]) < 0x80) || (ord($string[$length]) >= 0xC0)) - return substr($string, 0, $length); - - $length--; - } - return ""; - } - - /** - * Indicates if the specified folder type is a system folder - * - * @param int $foldertype - * - * @access public - * @return boolean - */ - static public function IsSystemFolder($foldertype) { - return ($foldertype == SYNC_FOLDER_TYPE_INBOX || $foldertype == SYNC_FOLDER_TYPE_DRAFTS || $foldertype == SYNC_FOLDER_TYPE_WASTEBASKET || $foldertype == SYNC_FOLDER_TYPE_SENTMAIL || - $foldertype == SYNC_FOLDER_TYPE_OUTBOX || $foldertype == SYNC_FOLDER_TYPE_TASK || $foldertype == SYNC_FOLDER_TYPE_APPOINTMENT || $foldertype == SYNC_FOLDER_TYPE_CONTACT || - $foldertype == SYNC_FOLDER_TYPE_NOTE || $foldertype == SYNC_FOLDER_TYPE_JOURNAL) ? true:false; - } - - /** - * Our own utf7_decode function because imap_utf7_decode converts a string - * into ISO-8859-1 encoding which doesn't have euro sign (it will be converted - * into two chars: [space](ascii 32) and "¬" ("not sign", ascii 172)). Also - * php iconv function expects '+' as delimiter instead of '&' like in IMAP. - * - * @param string $string IMAP folder name - * - * @access public - * @return string - */ - static public function Utf7_iconv_decode($string) { - //do not alter string if there aren't any '&' or '+' chars because - //it won't have any utf7-encoded chars and nothing has to be escaped. - if (strpos($string, '&') === false && strpos($string, '+') === false ) return $string; - - //Get the string length and go back through it making the replacements - //necessary - $len = strlen($string) - 1; - while ($len > 0) { - //look for '&-' sequence and replace it with '&' - if ($len > 0 && $string[$len-1] == '&' && $string[$len] == '-') { - $string = substr_replace($string, '&', $len - 1, 2); - $len--; //decrease $len as this char has alreasy been processed - } - //search for '&' which weren't found in if clause above and - //replace them with '+' as they mark an utf7-encoded char - if ($len > 0 && $string[($len-1)] == '&') { - $string = substr_replace($string, '+', $len - 1, 1); - $len--; //decrease $len as this char has alreasy been processed - } - //finally "escape" all remaining '+' chars - if ($len > 0 && $string[$len-1] == '+') { - $string = substr_replace($string, '+-', $len - 1, 1); - } - $len--; - } - return $string; - } - - /** - * Our own utf7_encode function because the string has to be converted from - * standard UTF7 into modified UTF7 (aka UTF7-IMAP). - * - * @param string $str IMAP folder name - * - * @access public - * @return string - */ - static public function Utf7_iconv_encode($string) { - //do not alter string if there aren't any '&' or '+' chars because - //it won't have any utf7-encoded chars and nothing has to be escaped. - if (strpos($string, '&') === false && strpos($string, '+') === false ) return $string; - - //Get the string length and go back through it making the replacements - //necessary - $len = strlen($string) - 1; - while ($len > 0) { - //look for '&-' sequence and replace it with '&' - if ($len > 0 && $string[$len-1] == '+' && $string[$len] == '-') { - $string = substr_replace($string, '+', $len - 1, 2); - $len--; //decrease $len as this char has alreasy been processed - } - //search for '&' which weren't found in if clause above and - //replace them with '+' as they mark an utf7-encoded char - if ($len > 0 && $string[$len-1] == '+') { - $string = substr_replace($string, '&', $len - 1, 1); - $len--; //decrease $len as this char has alreasy been processed - } - //finally "escape" all remaining '+' chars - if ($len > 0 && $string[$len-1] == '&') { - $string = substr_replace($string, '&-', $len - 1, 1); - } - $len--; - } - return $string; - } - - /** - * Converts an UTF-7 encoded string into an UTF-8 string. - * - * @param string $string to convert - * - * @access public - * @return string - */ - static public function Utf7_to_utf8($string) { - if (function_exists("iconv")){ - return @iconv("UTF-7", "UTF-8", $string); - } - else - ZLog::Write(LOGLEVEL_WARN, "Utils::Utf7_to_utf8() 'iconv' is not available. Charset conversion skipped."); - - return $string; - } - - /** - * Converts an UTF-8 encoded string into an UTF-7 string. - * - * @param string $string to convert - * - * @access public - * @return string - */ - static public function Utf8_to_utf7($string) { - if (function_exists("iconv")){ - return @iconv("UTF-8", "UTF-7", $string); - } - else - ZLog::Write(LOGLEVEL_WARN, "Utils::Utf8_to_utf7() 'iconv' is not available. Charset conversion skipped."); - - return $string; - } - - /** - * Checks for valid email addresses - * The used regex actually only checks if a valid email address is part of the submitted string - * it also returns true for the mailbox format, but this is not checked explicitly - * - * @param string $email address to be checked - * - * @access public - * @return boolean - */ - static public function CheckEmail($email) { - return (bool) preg_match('#([a-zA-Z0-9_\-])+(\.([a-zA-Z0-9_\-])+)*@((\[(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5]))\]))|((([a-zA-Z0-9])+(([\-])+([a-zA-Z0-9])+)*\.)+([a-zA-Z])+(([\-])+([a-zA-Z0-9])+)*)|localhost)#', $email); - } - - /** - * Checks for valid empty group of email - * e.g.: undisclosed-recipients:; - * - * @param string $email - * - * @access public - * @return boolean - */ - static public function CheckEmailEmptyGroup($email) { - return (bool) preg_match('/.*:;/', $email); - } - - /** - * Checks if a string is base64 encoded - * - * @param string $string the string to be checked - * - * @access public - * @return boolean - */ - static public function IsBase64String($string) { - return (bool) preg_match("#^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+/]{4})?$#", $string); - } - - /** - * Decodes base64 encoded query parameters. Based on dw2412 contribution. - * - * @param string $query the query to decode - * - * @access public - * @return array - */ - static public function DecodeBase64URI($query) { - /* - * The query string has a following structure. Number in () is position: - * 1 byte - protocoll version (0) - * 1 byte - command code (1) - * 2 bytes - locale (2) - * 1 byte - device ID length (4) - * variable - device ID (4+device ID length) - * 1 byte - policy key length (5+device ID length) - * 0 or 4 bytes - policy key (5+device ID length + policy key length) - * 1 byte - device type length (6+device ID length + policy key length) - * variable - device type (6+device ID length + policy key length + device type length) - * variable - command parameters, array which consists of: - * 1 byte - tag - * 1 byte - length - * variable - value of the parameter - * - */ - $decoded = base64_decode($query); - $devIdLength = ord($decoded[4]); //device ID length - $polKeyLength = ord($decoded[5+$devIdLength]); //policy key length - $devTypeLength = ord($decoded[6+$devIdLength+$polKeyLength]); //device type length - //unpack the decoded query string values - $unpackedQuery = unpack("CProtVer/CCommand/vLocale/CDevIDLen/H".($devIdLength*2)."DevID/CPolKeyLen".($polKeyLength == 4 ? "/VPolKey" : "")."/CDevTypeLen/A".($devTypeLength)."DevType", $decoded); - - //get the command parameters - $pos = 7 + $devIdLength + $polKeyLength + $devTypeLength; - $decoded = substr($decoded, $pos); - while (strlen($decoded) > 0) { - $paramLength = ord($decoded[1]); - $unpackedParam = unpack("CParamTag/CParamLength/A".$paramLength."ParamValue", $decoded); - $unpackedQuery[ord($decoded[0])] = $unpackedParam['ParamValue']; - //remove parameter from decoded query string - $decoded = substr($decoded, 2 + $paramLength); - } - return $unpackedQuery; - } - - /** - * Encode a string or a stream into a base64 string - * - * @param string or stream $input - * - * @access public - * @return string - */ - public static function EncodeBase64($input) { - if (is_resource($input)) { - ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): is_resource"); - if (defined('BUG68532FIXED') && BUG68532FIXED === true) { - ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): BUG68532FIXED"); - $stream = $input; - } elseif ( ($meta = stream_get_meta_data($input)) && $meta['stream_type'] === 'STDIO') { - ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): STDIO"); - $stream = $input; - } else { - ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): else"); - //because of bug #68532, we can't work with memory stream, - //so we copy input to a tmpfile - $stream = tmpfile(); - stream_copy_to_stream($input, $stream); - fclose($input); - rewind($stream); - } - $base64filter = stream_filter_append($stream, 'convert.base64-encode'); - $base64 = stream_get_contents($stream); - stream_filter_remove($base64filter); - fclose($stream); - return $base64; - } elseif (is_string($input)) { - ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): is_string"); - return base64_encode($input); - } else { - throw new Exception("unsupported type : ".gettype($input)); - } - } - - /** - * Returns a command string for a given command code. - * - * @param int $code - * - * @access public - * @return string or false if code is unknown - */ - public static function GetCommandFromCode($code) { - switch ($code) { - case ZPush::COMMAND_SYNC: return 'Sync'; - case ZPush::COMMAND_SENDMAIL: return 'SendMail'; - case ZPush::COMMAND_SMARTFORWARD: return 'SmartForward'; - case ZPush::COMMAND_SMARTREPLY: return 'SmartReply'; - case ZPush::COMMAND_GETATTACHMENT: return 'GetAttachment'; - case ZPush::COMMAND_FOLDERSYNC: return 'FolderSync'; - case ZPush::COMMAND_FOLDERCREATE: return 'FolderCreate'; - case ZPush::COMMAND_FOLDERDELETE: return 'FolderDelete'; - case ZPush::COMMAND_FOLDERUPDATE: return 'FolderUpdate'; - case ZPush::COMMAND_MOVEITEMS: return 'MoveItems'; - case ZPush::COMMAND_GETITEMESTIMATE: return 'GetItemEstimate'; - case ZPush::COMMAND_MEETINGRESPONSE: return 'MeetingResponse'; - case ZPush::COMMAND_SEARCH: return 'Search'; - case ZPush::COMMAND_SETTINGS: return 'Settings'; - case ZPush::COMMAND_PING: return 'Ping'; - case ZPush::COMMAND_ITEMOPERATIONS: return 'ItemOperations'; - case ZPush::COMMAND_PROVISION: return 'Provision'; - case ZPush::COMMAND_RESOLVERECIPIENTS: return 'ResolveRecipients'; - case ZPush::COMMAND_VALIDATECERT: return 'ValidateCert'; - - // Deprecated commands (AS >= 14) - case ZPush::COMMAND_GETHIERARCHY: return 'GetHierarchy'; - - // Webservice commands - case ZPush::COMMAND_WEBSERVICE_DEVICE: return 'WebserviceDevice'; - case ZPush::COMMAND_WEBSERVICE_USERS: return 'WebserviceUsers'; - } - return false; - } - - /** - * Returns a command code for a given command. - * - * @param string $command - * - * @access public - * @return int or false if command is unknown - */ - public static function GetCodeFromCommand($command) { - switch ($command) { - case 'Sync': return ZPush::COMMAND_SYNC; - case 'SendMail': return ZPush::COMMAND_SENDMAIL; - case 'SmartForward': return ZPush::COMMAND_SMARTFORWARD; - case 'SmartReply': return ZPush::COMMAND_SMARTREPLY; - case 'GetAttachment': return ZPush::COMMAND_GETATTACHMENT; - case 'FolderSync': return ZPush::COMMAND_FOLDERSYNC; - case 'FolderCreate': return ZPush::COMMAND_FOLDERCREATE; - case 'FolderDelete': return ZPush::COMMAND_FOLDERDELETE; - case 'FolderUpdate': return ZPush::COMMAND_FOLDERUPDATE; - case 'MoveItems': return ZPush::COMMAND_MOVEITEMS; - case 'GetItemEstimate': return ZPush::COMMAND_GETITEMESTIMATE; - case 'MeetingResponse': return ZPush::COMMAND_MEETINGRESPONSE; - case 'Search': return ZPush::COMMAND_SEARCH; - case 'Settings': return ZPush::COMMAND_SETTINGS; - case 'Ping': return ZPush::COMMAND_PING; - case 'ItemOperations': return ZPush::COMMAND_ITEMOPERATIONS; - case 'Provision': return ZPush::COMMAND_PROVISION; - case 'ResolveRecipients': return ZPush::COMMAND_RESOLVERECIPIENTS; - case 'ValidateCert': return ZPush::COMMAND_VALIDATECERT; - - // Deprecated commands (AS >= 14) - case 'GetHierarchy': return ZPush::COMMAND_GETHIERARCHY; - - // Webservice commands - case 'WebserviceDevice': return ZPush::COMMAND_WEBSERVICE_DEVICE; - case 'WebserviceUsers': return ZPush::COMMAND_WEBSERVICE_USERS; - } - return false; - } - - /** - * Normalize the given timestamp to the start of the day - * - * @param long $timestamp - * - * @access private - * @return long - */ - public static function getDayStartOfTimestamp($timestamp) { - return $timestamp - ($timestamp % (60 * 60 * 24)); - } - - /** - * Returns a formatted string output from an optional timestamp. - * If no timestamp is sent, NOW is used. - * - * @param long $timestamp - * - * @access public - * @return string - */ - public static function GetFormattedTime($timestamp = false) { - if (!$timestamp) - return @strftime("%d/%m/%Y %H:%M:%S"); - else - return @strftime("%d/%m/%Y %H:%M:%S", $timestamp); - } - - /** - * Returns a formatted string output from an optional timestamp with microseconds. - * If no timestamp is sent, NOW is used. - * - * @param float $timestamp - * - * @access public - * @return string - */ - public static function GetFormattedMicroTime($timestamp = false) { - if(!$timestamp) - $timestamp = microtime(true); - - $t = explode('.',number_format($timestamp,6,'.',''),2); - return strftime("%Y-%m-%dT%H:%M:%S", $t[0]).'.'.$t[1]; - } - - /** - * Get charset name from a codepage - * - * @see http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx - * - * Table taken from common/codepage.cpp - * - * @param integer codepage Codepage - * - * @access public - * @return string iconv-compatible charset name - */ - public static function GetCodepageCharset($codepage) { - $codepages = array( - 20106 => "DIN_66003", - 20108 => "NS_4551-1", - 20107 => "SEN_850200_B", - 950 => "big5", - 50221 => "csISO2022JP", - 51932 => "euc-jp", - 51936 => "euc-cn", - 51949 => "euc-kr", - 949 => "euc-kr", - 936 => "gb18030", - 52936 => "csgb2312", - 852 => "ibm852", - 866 => "ibm866", - 50220 => "iso-2022-jp", - 50222 => "iso-2022-jp", - 50225 => "iso-2022-kr", - 1252 => "windows-1252", - 28591 => "iso-8859-1", - 28592 => "iso-8859-2", - 28593 => "iso-8859-3", - 28594 => "iso-8859-4", - 28595 => "iso-8859-5", - 28596 => "iso-8859-6", - 28597 => "iso-8859-7", - 28598 => "iso-8859-8", - 28599 => "iso-8859-9", - 28603 => "iso-8859-13", - 28605 => "iso-8859-15", - 20866 => "koi8-r", - 21866 => "koi8-u", - 932 => "shift-jis", - 1200 => "unicode", - 1201 => "unicodebig", - 65000 => "utf-7", - 65001 => "utf-8", - 1250 => "windows-1250", - 1251 => "windows-1251", - 1253 => "windows-1253", - 1254 => "windows-1254", - 1255 => "windows-1255", - 1256 => "windows-1256", - 1257 => "windows-1257", - 1258 => "windows-1258", - 874 => "windows-874", - 20127 => "us-ascii" - ); - - if(isset($codepages[$codepage])) { - return $codepages[$codepage]; - } else { - // Defaulting to iso-8859-15 since it is more likely for someone to make a mistake in the codepage - // when using west-european charsets then when using other charsets since utf-8 is binary compatible - // with the bottom 7 bits of west-european - return "iso-8859-15"; - } - } - - /** - * Converts a string encoded with codepage into an UTF-8 string - * - * @param int $codepage - * @param string $string - * - * @access public - * @return string - */ - public static function ConvertCodepageStringToUtf8($codepage, $string) { - if (function_exists("iconv")) { - $charset = self::GetCodepageCharset($codepage); - return iconv($charset, "utf-8", $string); - } - else - ZLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepageStringToUtf8() 'iconv' is not available. Charset conversion skipped."); - - return $string; - } - - /** - * Converts a string to another charset. - * - * @param int $in - * @param int $out - * @param string $string - * - * @access public - * @return string - */ - public static function ConvertCodepage($in, $out, $string) { - // do nothing if both charsets are the same - if ($in == $out) - return $string; - - if (function_exists("iconv")) { - $inCharset = self::GetCodepageCharset($in); - $outCharset = self::GetCodepageCharset($out); - return iconv($inCharset, $outCharset, $string); - } - else - ZLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepage() 'iconv' is not available. Charset conversion skipped."); - - return $string; - } - - /** - * Returns the best match of preferred body preference types. - * - * @param array $bpTypes - * - * @access public - * @return int - */ - public static function GetBodyPreferenceBestMatch($bpTypes) { - // The best choice is RTF, then HTML and then MIME in order to save bandwidth - // because MIME is a complete message including the headers and attachments - if (in_array(SYNC_BODYPREFERENCE_RTF, $bpTypes)) return SYNC_BODYPREFERENCE_RTF; - if (in_array(SYNC_BODYPREFERENCE_HTML, $bpTypes)) return SYNC_BODYPREFERENCE_HTML; - if (in_array(SYNC_BODYPREFERENCE_MIME, $bpTypes)) return SYNC_BODYPREFERENCE_MIME; - return SYNC_BODYPREFERENCE_PLAIN; - } - - /* BEGIN fmbiete's contribution r1516, ZP-318 */ - /** - * Converts a html string into a plain text string - * - * @param string $html - * - * @access public - * @return string - */ - public static function ConvertHtmlToText($html) { - // remove css-style tags - $plaintext = preg_replace("//is", "", $html); - // remove all other html - $plaintext = strip_tags($plaintext); - - return $plaintext; - } - /* END fmbiete's contribution r1516, ZP-318 */ - - /** - * Checks if a file has the same owner and group as the parent directory. - * If not, owner and group are fixed (being updated to the owner/group of the directory). - * Function code contributed by Robert Scheck aka rsc. - * - * @param string $file - * - * @access public - * @return boolean - */ - public static function FixFileOwner($file) { - if(posix_getuid() == 0 && file_exists($file)) { - $dir = dirname($file); - $perm_dir = stat($dir); - $perm_log = stat($file); - - if($perm_dir[4] !== $perm_log[4] || $perm_dir[5] !== $perm_log[5]) { - chown($file, $perm_dir[4]); - chgrp($file, $perm_dir[5]); - } - } - return true; - } - - /** - * Returns AS-style LastVerbExecuted value from the server value. - * - * @param int $verb - * - * @access public - * @return int - */ - public static function GetLastVerbExecuted($verb) { - switch ($verb) { - case NOTEIVERB_REPLYTOSENDER: return AS_REPLYTOSENDER; - case NOTEIVERB_REPLYTOALL: return AS_REPLYTOALL; - case NOTEIVERB_FORWARD: return AS_FORWARD; - } - - return 0; - } - - /** - * Returns the local part from email address. - * - * @param string $email - * - * @access public - * @return string - */ - public static function GetLocalPartFromEmail($email) { - $pos = strpos($email, '@'); - if ($pos === false) { - return $email; - } - return substr($email, 0, $pos); - } - - /** - * Generate date object from string and timezone. - * - * @param string $value - * @param string $timezone - * - * @access public - * @return int epoch - */ - public static function MakeUTCDate($value, $timezone = null) { - $tz = null; - if ($timezone) { - $tz = timezone_open($timezone); - } - if (!$tz) { - //If there is no timezone set, we use the default timezone - $tz = timezone_open(date_default_timezone_get()); - } - //20110930T090000Z - $date = date_create_from_format('Ymd\THis\Z', $value, timezone_open("UTC")); - if (!$date) { - //20110930T090000 - $date = date_create_from_format('Ymd\THis', $value, $tz); - } - if (!$date) { - //20110930 (Append T000000Z to the date, so it starts at midnight) - $date = date_create_from_format('Ymd\THis\Z', $value . "T000000Z", $tz); - } - return date_timestamp_get($date); - } - - - /** - * Generate a tzid from various formats - * - * @param str $timezone - * - * @access public - * @return timezone id - */ - public static function ParseTimezone($timezone) { - //(GMT+01.00) Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna - if (preg_match('/GMT(\\+|\\-)0(\d)/', $timezone, $matches)) { - return "Etc/GMT" . $matches[1] . $matches[2]; - } - //(GMT+10.00) XXX / XXX / XXX / XXX - if (preg_match('/GMT(\\+|\\-)1(\d)/', $timezone, $matches)) { - return "Etc/GMT" . $matches[1] . "1" . $matches[2]; - } - ///inverse.ca/20101018_1/Europe/Amsterdam or /inverse.ca/20101018_1/America/Argentina/Buenos_Aires - if (preg_match('/\/[.[:word:]]+\/\w+\/(\w+)\/([\w\/]+)/', $timezone, $matches)) { - return $matches[1] . "/" . $matches[2]; - } - return TimezoneUtil::getMSTZnameFromTZName(trim($timezone, '"')); - } - - /** - * Safely write data to disk, using an unique tmp file (concurrent write), - * and using rename for atomicity - * - * If you use safe_put_contents, you can safely use file_get_contents - * (you will always read a fully written file) - * - * @param string $filename - * @param string $data - * @return boolean|int - */ - public static function safe_put_contents($filename, $data) { - //put the 'tmp' as a prefix (and not suffix) so all glob call will not see temp files - $tmp = dirname($filename) . DIRECTORY_SEPARATOR . 'tmp-' . getmypid() . '-' . basename($filename); - if (($res = file_put_contents($tmp, $data)) !== false) - if (rename($tmp, $filename) !== true) - $res = false; - - return $res; - } -} \ No newline at end of file diff --git a/sources/lib/utils/zpushadmin.php b/sources/lib/utils/zpushadmin.php deleted file mode 100644 index e78ad2b..0000000 --- a/sources/lib/utils/zpushadmin.php +++ /dev/null @@ -1,721 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class ZPushAdmin { - /** - * //TODO resync of a foldertype for all users (e.g. Appointment) - */ - - /** - * List devices known to Z-Push. - * If no user is given, all devices are listed - * - * @param string $user devices of that user, if false all devices of all users - * - * @return array - * @access public - */ - static public function ListDevices($user = false) { - return ZPush::GetStateMachine()->GetAllDevices($user); - } - - /** - * List users of a device known to Z-Push. - * - * @param string $devid users of that device - * - * @return array - * @access public - */ - static public function ListUsers($devid) { - try { - $devState = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - - if ($devState instanceof StateObject && isset($devState->devices) && is_array($devState->devices)) - return array_keys($devState->devices); - else - return array(); - } - catch (StateNotFoundException $stnf) { - return array(); - } - } - - /** - * Returns details of a device like synctimes, - * policy and wipe status, synched folders etc - * - * @param string $devid device id - * @param string $user user to be looked up - * - * @return ASDevice object - * @access public - */ - static public function GetDeviceDetails($devid, $user) { - - try { - $device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED); - $device->SetData(ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA), false); - $device->StripData(); - - try { - // we need a StateManager for this operation - $stateManager = new StateManager(); - $stateManager->SetDevice($device); - - $sc = new SyncCollections(); - $sc->SetStateManager($stateManager); - - // load all collections of device without loading states or checking permissions - $sc->LoadAllCollections(true, false, false); - - if ($sc->GetLastSyncTime()) - $device->SetLastSyncTime($sc->GetLastSyncTime()); - - // get information about the folder synchronization status from SyncCollections - $folders = $device->GetAllFolderIds(); - - foreach ($folders as $folderid) { - $fstatus = $device->GetFolderSyncStatus($folderid); - - if ($fstatus !== false && isset($fstatus[ASDevice::FOLDERSYNCSTATUS])) { - $spa = $sc->GetCollection($folderid); - $total = $spa->GetFolderSyncTotal(); - $todo = $spa->GetFolderSyncRemaining(); - $fstatus['status'] = ($fstatus[ASDevice::FOLDERSYNCSTATUS] == 1)?'Initialized':'Synchronizing'; - $fstatus['total'] = $total; - $fstatus['done'] = $total - $todo; - $fstatus['todo'] = $todo; - - $device->SetFolderSyncStatus($folderid, $fstatus); - } - } - } - catch (StateInvalidException $sive) { - ZLog::Write(LOGLEVEL_WARN, sprintf("ZPushAdmin::GetDeviceDetails(): device '%s' of user '%s' has invalid states. Please sync to solve this issue.", $devid, $user)); - $device->SetDeviceError("Invalid states. Please force synchronization!"); - } - - return $device; - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::GetDeviceDetails(): device '%s' of user '%s' can not be found", $devid, $user)); - return false; - } - } - - /** - * Wipes 'a' or all devices of a user. - * If no user is set, the device is generally wiped. - * If no device id is set, all devices of the user will be wiped. - * Device id or user must be set! - * - * @param string $requestedBy user which requested this operation - * @param string $user (opt)user of the device - * @param string $devid (opt) device id which should be wiped - * - * @return boolean - * @access public - */ - static public function WipeDevice($requestedBy, $user, $devid = false) { - if ($user === false && $devid === false) - return false; - - if ($devid === false) { - $devicesIds = ZPush::GetStateMachine()->GetAllDevices($user); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::WipeDevice(): all '%d' devices for user '%s' found to be wiped", count($devicesIds), $user)); - foreach ($devicesIds as $deviceid) { - if (!self::WipeDevice($requestedBy, $user, $deviceid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::WipeDevice(): wipe devices failed for device '%s' of user '%s'. Aborting.", $deviceid, $user)); - return false; - } - } - } - - // wipe a device completely (for connected users to this device) - else if ($devid !== false && $user === false) { - $users = self::ListUsers($devid); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::WipeDevice(): device '%d' is used by '%d' users and will be wiped", $devid, count($users))); - if (count($users) == 0) - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::WipeDevice(): no user found on device '%s'. Aborting.", $devid)); - - return self::WipeDevice($requestedBy, $users[0], $devid); - } - - else { - // load device data - $device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED); - try { - $device->SetData(ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA), false); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::WipeDevice(): device '%s' of user '%s' can not be found", $devid, $user)); - return false; - } - - // set wipe status - if ($device->GetWipeStatus() == SYNC_PROVISION_RWSTATUS_WIPED) - ZLog::Write(LOGLEVEL_INFO, sprintf("ZPushAdmin::WipeDevice(): device '%s' of user '%s' was alread sucessfully remote wiped on %s", $devid , $user, strftime("%Y-%m-%d %H:%M", $device->GetWipeActionOn()))); - else - $device->SetWipeStatus(SYNC_PROVISION_RWSTATUS_PENDING, $requestedBy); - - // save device data - try { - if ($device->IsNewDevice()) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::WipeDevice(): data of user '%s' not synchronized on device '%s'. Aborting.", $user, $devid)); - return false; - } - - ZPush::GetStateMachine()->SetState($device->GetData(), $devid, IStateMachine::DEVICEDATA); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::WipeDevice(): device '%s' of user '%s' marked to be wiped", $devid, $user)); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::WipeDevice(): state for device '%s' of user '%s' can not be saved", $devid, $user)); - return false; - } - } - return true; - } - - - /** - * Removes device details from the z-push directory. - * If device id is not set, all devices of a user are removed. - * If the user is not set, the details of the device (independently if used by several users) is removed. - * Device id or user must be set! - * - * @param string $user (opt) user of the device - * @param string $devid (opt) device id which should be wiped - * - * @return boolean - * @access public - */ - static public function RemoveDevice($user = false, $devid = false) { - if ($user === false && $devid === false) - return false; - - // remove all devices for user - if ($devid === false && $user !== false) { - $devicesIds = ZPush::GetStateMachine()->GetAllDevices($user); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): all '%d' devices for user '%s' found to be removed", count($devicesIds), $user)); - foreach ($devicesIds as $deviceid) { - if (!self::RemoveDevice($user, $deviceid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing devices failed for device '%s' of user '%s'. Aborting", $deviceid, $user)); - return false; - } - } - } - // remove a device completely (for connected users to this device) - else if ($devid !== false && $user === false) { - $users = self::ListUsers($devid); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): device '%d' is used by '%d' users and will be removed", $devid, count($users))); - foreach ($users as $aUser) { - if (!self::RemoveDevice($aUser, $devid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing user '%s' from device '%s' failed. Aborting", $aUser, $devid)); - return false; - } - } - } - - // user and deviceid set - else { - // load device data - $device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED); - $devices = array(); - try { - $devicedata = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - $device->SetData($devicedata, false); - if (!isset($devicedata->devices)) - throw new StateInvalidException("No devicedata stored in ASDevice"); - $devices = $devicedata->devices; - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): device '%s' of user '%s' can not be found", $devid, $user)); - return false; - } - - // remove all related states - foreach ($device->GetAllFolderIds() as $folderid) - StateManager::UnLinkState($device, $folderid); - - // remove hierarchcache - StateManager::UnLinkState($device, false); - - // remove backend storage permanent data - ZPush::GetStateMachine()->CleanStates($device->GetDeviceId(), IStateMachine::BACKENDSTORAGE, false, 99999999999); - - // remove devicedata and unlink user from device - unset($devices[$user]); - if (isset($devicedata->devices)) - $devicedata->devices = $devices; - ZPush::GetStateMachine()->UnLinkUserDevice($user, $devid); - - // no more users linked for device - remove device data - if (count($devices) == 0) - ZPush::GetStateMachine()->CleanStates($devid, IStateMachine::DEVICEDATA, false); - - // save data if something left - else - ZPush::GetStateMachine()->SetState($devicedata, $devid, IStateMachine::DEVICEDATA); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): data of device '%s' of user '%s' removed", $devid, $user)); - } - return true; - } - - - /** - * Marks a folder of a device of a user for re-synchronization - * - * @param string $user user of the device - * @param string $devid device id which should be wiped - * @param mixed $folderid a single folder id or an array of folder ids - * - * @return boolean - * @access public - */ - static public function ResyncFolder($user, $devid, $folderid) { - // load device data - $device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED); - try { - $device->SetData(ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA), false); - - if ($device->IsNewDevice()) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncFolder(): data of user '%s' not synchronized on device '%s'. Aborting.",$user, $devid)); - return false; - } - - if (!$folderid || (is_array($folderid) && empty($folderid))) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncFolder(): no folders synchronized for user '%s' on device '%s'. Aborting.",$user, $devid)); - return false; - } - - // remove folder state - if (is_array($folderid)) { - foreach ($folderid as $fid) { - StateManager::UnLinkState($device, $fid); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::ResyncFolder(): folder '%s' on device '%s' of user '%s' marked to be re-synchronized.", $fid, $devid, $user)); - } - } - else { - StateManager::UnLinkState($device, $folderid); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::ResyncFolder(): folder '%s' on device '%s' of user '%s' marked to be re-synchronized.", $folderid, $devid, $user)); - } - - ZPush::GetStateMachine()->SetState($device->GetData(), $devid, IStateMachine::DEVICEDATA); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::ResyncFolder(): saved updated device data of device '%s' of user '%s'", $devid, $user)); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncFolder(): state for device '%s' of user '%s' can not be found or saved", $devid, $user)); - return false; - } - return true; - } - - - /** - * Marks a all folders synchronized to a device for re-synchronization - * If no user is set all user which are synchronized for a device are marked for re-synchronization. - * If no device id is set all devices of that user are marked for re-synchronization. - * If no user and no device are set then ALL DEVICES are marked for resynchronization (use with care!). - * - * @param string $user (opt) user of the device - * @param string $devid (opt)device id which should be wiped - * - * @return boolean - * @access public - */ - static public function ResyncDevice($user, $devid = false) { - - // search for target devices - if ($devid === false) { - $devicesIds = ZPush::GetStateMachine()->GetAllDevices($user); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::ResyncDevice(): all '%d' devices for user '%s' found to be re-synchronized", count($devicesIds), $user)); - foreach ($devicesIds as $deviceid) { - if (!self::ResyncDevice($user, $deviceid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncDevice(): wipe devices failed for device '%s' of user '%s'. Aborting", $deviceid, $user)); - return false; - } - } - } - else { - // get devicedata - try { - $devicedata = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncDevice(): state for device '%s' can not be found", $devid)); - return false; - - } - - // loop through all users which currently use this device - if ($user === false && $devicedata instanceof StateObject && isset($devicedata->devices) && - is_array($devicedata->devices) && count($devicedata->devices) > 1) { - foreach (array_keys($devicedata) as $aUser) { - if (!self::ResyncDevice($aUser, $devid)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncDevice(): re-synchronization failed for device '%s' of user '%s'. Aborting", $devid, $aUser)); - return false; - } - } - } - - // load device data - $device = new ASDevice($devid, ASDevice::UNDEFINED, $user, ASDevice::UNDEFINED); - try { - $device->SetData($devicedata, false); - - if ($device->IsNewDevice()) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncDevice(): data of user '%s' not synchronized on device '%s'. Aborting.",$user, $devid)); - return false; - } - - // delete all uuids - foreach ($device->GetAllFolderIds() as $folderid) - StateManager::UnLinkState($device, $folderid); - - // remove hierarchcache - StateManager::UnLinkState($device, false); - - ZPush::GetStateMachine()->SetState($device->GetData(), $devid, IStateMachine::DEVICEDATA); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::ResyncDevice(): all folders synchronized to device '%s' of user '%s' marked to be re-synchronized.", $devid, $user)); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::ResyncDevice(): state for device '%s' of user '%s' can not be found or saved", $devid, $user)); - return false; - } - } - return true; - } - - /** - * Clears loop detection data - * - * @param string $user (opt) user which data should be removed - user may not be specified without device id - * @param string $devid (opt) device id which data to be removed - * - * @return boolean - * @access public - */ - static public function ClearLoopDetectionData($user = false, $devid = false) { - return ZPush::GetLoopDetection()->ClearData($user, $devid); - } - - /** - * Returns loop detection data of a user & device - * - * @param string $user - * @param string $devid - * - * @return array/boolean returns false if data is not available - * @access public - */ - static public function GetLoopDetectionData($user, $devid) { - return ZPush::GetLoopDetection()->GetCachedData($user, $devid); - } - - /** - * Remove users that doesn't belong to this devicedata state - * - * @return boolean - * @access public - */ - public static function FixStatesWrongDevicedata() { - $statesfixed = 0; - $statesok = 0; - $usersremoved = 0; - $usersok = 0; - $devices = ZPush::GetStateMachine()->GetAllDevices(false); - foreach ($devices as $devid) { - $changed = false; - $devicedata = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - if (!($devicedata instanceof StateObject && isset($devicedata->devices) && is_array($devicedata->devices))) - continue; - - $asdevices = $devicedata->devices; - foreach ($asdevices as $asuser => $asdev) { - $asdevid = $asdev->GetDeviceId(); - if (strcasecmp($asdevid, $devid) !== 0) { - $usersremoved++; - $changed = true; - unset($asdevices[$asuser]); - ZPush::GetStateMachine()->UnLinkUserDevice($asuser, $devid); - ZLog::Write(LOGLEVEL_WARN, sprintf("Removed from %s devicedata state user '%s' (devid = '%s')", $devid, $asuser, $asdevid)); - } else { - $usersok++; - } - } - if ($changed) { - $statesfixed++; - $devicedata->devices = $asdevices; - ZPush::GetStateMachine()->SetState($devicedata, $devid, IStateMachine::DEVICEDATA); - } else { - $statesok++; - } - } - return array($statesfixed, $statesok, $usersremoved, $usersok); - } - - /** - * Fixes states with usernames in different cases - * - * @return boolean - * @access public - */ - static public function FixStatesDifferentUsernameCases() { - $processed = 0; - $dropedUsers = 0; - $fixedUsers = 0; - - $devices = ZPush::GetStateMachine()->GetAllDevices(false); - foreach ($devices as $devid) { - $users = self::ListUsers($devid); - $obsoleteUsers = array(); - - // find obsolete uppercase users - foreach ($users as $username) { - $processed++; - $lowUsername = strtolower($username); - if ($lowUsername === $username) - continue; // default case - - $obsoleteUsers[] = $username; - } - - // remove or transform obsolete users - if (!empty($obsoleteUsers)) { - // load the device data - try { - $devData = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - - $devices = $devData->devices; - $knownUsers = array_keys($devData->devices); - ZLog::Write(LOGLEVEL_DEBUG, print_r($devData,1),false); - - foreach ($obsoleteUsers as $ouser) { - $lowerOUser = strtolower($ouser); - // there is a lowercase user, drop the uppercase one - if (in_array($lowerOUser, $knownUsers)) { - unset($devices[$ouser]); - $dropedUsers++; - ZLog::Write(LOGLEVEL_DEBUG, print_r(array_keys($devices),1)); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesDifferentUsernameCases(): user '%s' of device '%s' is obsolete as a lowercase username is known", $ouser, $devid)); - } - // there is only an uppercase user, save it as lowercase - else { - $devices[$lowerOUser] = $devices[$ouser]; - unset($devices[$ouser]); - $fixedUsers++; - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesDifferentUsernameCases(): user '%s' of device '%s' was saved as '%s'", $ouser, $devid, $lowerOUser)); - } - } - - unset($devData->device); - // save the devicedata - $devData->devices = $devices; - ZPush::GetStateMachine()->SetState($devData, $devid, IStateMachine::DEVICEDATA); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesDifferentUsernameCases(): updated device '%s' and user(s) %s were dropped or converted", $devid, implode(", ", $obsoleteUsers))); - } - catch (StateNotFoundException $e) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::FixStatesDifferentUsernameCases(): state for device '%s' can not be found", $devid)); - } - } - } - - return array($processed, $fixedUsers, $dropedUsers); - } - - /** - * Fixes states of available device data to the user linking - * - * @return array - * @access public - */ - public static function FixStatesDeviceToUserLinking() { - //users to devices mapping - $usersdevs = ZPush::GetStateMachine()->GetAllUserDevice(); - $devsusers = array(); - foreach ($usersdevs as $user => $devs) { - foreach (array_keys($devs) as $dev) { - if (empty($devsusers[$dev])) - $devsusers[$dev] = array(); - $devsusers[$dev][] = $user; - } - } - unset($usersdevs); - - $linked = 0; - $unlinked = 0; - //devices to users mapping - $statedevices = ZPush::GetStateMachine()->GetAllDevices(false); - $alldevices = array_merge($statedevices, array_keys($devsusers)); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesDeviceToUserLinking(): found %d devices (%d|%d)", count($alldevices), count($statedevices), count($devsusers))); - - foreach ($alldevices as $devid) { - $stateusers = self::ListUsers($devid); - $mapusers = isset($devsusers[$devid]) ? $devsusers[$devid] : array(); - $links = array_diff($stateusers, $mapusers); - foreach ($links as $user) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ZPushAdmin::FixStatesDeviceToUserLinking(): linking user '%s' to device '%s'", $user, $devid)); - ZPush::GetStateMachine()->LinkUserDevice($user, $devid); - $linked++; - } - $unlinks = array_diff($stateusers, $mapusers); - foreach ($unlinks as $user) { - ZLog::Write(LOGLEVEL_INFO, sprintf("ZPushAdmin::FixStatesDeviceToUserLinking(): unlinking user '%s' to device '%s'", $user, $devid)); - ZPush::GetStateMachine()->UnLinkUserDevice($user, $devid); - $unlinked++; - } - } - return array($unlinked, $linked); - } - - /** - * Fixes states of the user linking to the states - * and removes all obsolete states - * - * @return boolean - * @access public - */ - static public function FixStatesUserToStatesLinking() { - $processed = 0; - $deleted = 0; - $devices = ZPush::GetStateMachine()->GetAllDevices(false); - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesUserToStatesLinking(): found %d devices", count($devices))); - - foreach ($devices as $devid) { - try { - // we work on device level - $devicedata = ZPush::GetStateMachine()->GetState($devid, IStateMachine::DEVICEDATA); - $knownUuids = array(); - - // == self::ListUsers (no need to GetState 2 times) - if ($devicedata instanceof StateObject && isset($devicedata->devices) && is_array($devicedata->devices)) - $usernames = array_keys($devicedata->devices); - else - $usernames = array(); - // get all known UUIDs for this device - foreach ($usernames as $username) { - $device = new ASDevice($devid, ASDevice::UNDEFINED, $username, ASDevice::UNDEFINED); - $device->SetData($devicedata, false); - - // get all known uuids of this device - $folders = $device->GetAllFolderIds(); - - // add a "false" folder id so the hierarchy UUID is retrieved - $folders[] = false; - - foreach ($folders as $folderid) { - $uuid = $device->GetFolderUUID($folderid); - if ($uuid) - $knownUuids[] = $uuid; - } - - } - } - catch (StateNotFoundException $e) {} - - // get all uuids for deviceid from statemachine - $existingStates = ZPush::GetStateMachine()->GetAllStatesForDevice($devid); - $processed = count($existingStates); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::FixStatesUserToStatesLinking(): found %d valid uuids and %d states for device device '%s'", count($knownUuids), $processed, $devid)); - - // remove states for all unknown uuids - foreach ($existingStates as $obsoleteState) { - if ($obsoleteState['type'] === IStateMachine::DEVICEDATA) - continue; - - if (!in_array($obsoleteState['uuid'], $knownUuids)) { - if (is_numeric($obsoleteState['counter'])) - $obsoleteState['counter']++; - - ZPush::GetStateMachine()->CleanStates($devid, $obsoleteState['type'], $obsoleteState['uuid'], $obsoleteState['counter']); - $deleted++; - } - } - } - return array($processed, $deleted); - } - - /** - * Maps a username for a specific backend to another username. - * - * @param string $user The username - * @param string $backend Name of the backend to map - * @param string $targetUsername The username to actually use for this backend - * - * @return boolean - */ - static public function AddUsernameMapping($user, $backend, $targetUsername) { - if (!ZPush::GetStateMachine()->MapUsername($user, $backend, $targetUsername)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::AddUsernameMapping(): unable to add mapping for user %s and backend %s", $user, $backend)); - return false; - } - - ZLog::Write(LOGLEVEL_INFO, sprintf("ZPushAdmin::AddUsernameMapping(): successfully mapped user %s and backend %s to username %s", $user, $backend, $targetUsername)); - return true; - } - - /** - * Unmaps a username for a specific backend. - * - * @param string $user The username - * @param string $backend Name of the backend to unmap - * - * @return boolean - */ - static public function RemoveUsernameMapping($user, $backend) { - if (!ZPush::GetStateMachine()->UnmapUsername($user, $backend)) { - ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveUsernameMapping(): unable to remove mapping for user %s and backend %s", $user, $backend)); - return false; - } - - ZLog::Write(LOGLEVEL_INFO, sprintf("ZPushAdmin::RemoveUsernameMapping(): successfully unmapped user %s and backend %s", $user, $backend)); - return true; - } -} diff --git a/sources/lib/wbxml/wbxmldecoder.php b/sources/lib/wbxml/wbxmldecoder.php deleted file mode 100644 index af356d0..0000000 --- a/sources/lib/wbxml/wbxmldecoder.php +++ /dev/null @@ -1,476 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class WBXMLDecoder extends WBXMLDefs { - private $in; - private $inLog; - private $tagcp = 0; - private $ungetbuffer; - private $log = false; - private $logStack = array(); - private $inputBuffer = ""; - private $isWBXML = true; - - const VERSION = 0x03; - - /** - * WBXML Decode Constructor - * We only handle ActiveSync WBXML, which is a subset of WBXML - * - * @param stream $input the incoming data stream - * - * @access public - */ - public function WBXMLDecoder($input) { - $this->log = defined('WBXML_DEBUG') && WBXML_DEBUG; - - $this->in = $input; - $this->inLog = StringStreamWrapper::Open(""); - - $version = $this->getByte(); - if($version != self::VERSION) { - $this->inputBuffer .= chr($version); - $this->isWBXML = false; - return; - } - - $publicid = $this->getMBUInt(); - if($publicid !== 1) - throw new WBXMLException("Wrong publicid : ".$publicid); - - $charsetid = $this->getMBUInt(); - if ($charsetid !== 106) - throw new WBXMLException("Wrong charset : ".$charsetid); - - $stringtablesize = $this->getMBUInt(); - if ($stringtablesize !== 0) - throw new WBXMLException("Wrong string table size : ".$stringtablesize); - } - - /** - * Returns either start, content or end, and auto-concatenates successive content - * - * @access public - * @return element/value - */ - public function getElement() { - $element = $this->getToken(); - - switch($element[EN_TYPE]) { - case EN_TYPE_STARTTAG: - return $element; - case EN_TYPE_ENDTAG: - return $element; - case EN_TYPE_CONTENT: - while(1) { - $next = $this->getToken(); - if($next == false) - return false; - else if($next[EN_TYPE] == EN_CONTENT) { - $element[EN_CONTENT] .= $next[EN_CONTENT]; - } else { - $this->ungetElement($next); - break; - } - } - return $element; - } - - return false; - } - - /** - * Get a peek at the next element - * - * @access public - * @return element - */ - public function peek() { - $element = $this->getElement(); - $this->ungetElement($element); - return $element; - } - - /** - * Get the element of a StartTag - * - * @param $tag - * - * @access public - * @return element/boolean returns false if not available - */ - public function getElementStartTag($tag) { - $element = $this->getToken(); - - if($element[EN_TYPE] == EN_TYPE_STARTTAG && $element[EN_TAG] == $tag) - return $element; - else { - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementStartTag(): unmatched WBXML tag: '%s' matching '%s' type '%s' flags '%s'", $tag, ((isset($element[EN_TAG]))?$element[EN_TAG]:""), ((isset($element[EN_TYPE]))?$element[EN_TYPE]:""), ((isset($element[EN_FLAGS]))?$element[EN_FLAGS]:""))); - $this->ungetElement($element); - } - - return false; - } - - /** - * Get the element of a EndTag - * - * @access public - * @return element/boolean returns false if not available - */ - public function getElementEndTag() { - $element = $this->getToken(); - - if($element[EN_TYPE] == EN_TYPE_ENDTAG) - return $element; - else { - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementEndTag(): unmatched WBXML tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG]))?$element[EN_TAG]:""), ((isset($element[EN_TYPE]))?$element[EN_TYPE]:""), ((isset($element[EN_FLAGS]))?$element[EN_FLAGS]:""))); - - $bt = debug_backtrace(); - ZLog::Write(LOGLEVEL_ERROR, sprintf("WBXMLDecoder->getElementEndTag(): could not read end tag in '%s'. Please enable the LOGLEVEL_WBXML and send the log to the Z-Push dev team.", $bt[0]["file"] . ":" . $bt[0]["line"])); - - // log the remaining wbxml content - $this->ungetElement($element); - while($el = $this->getElement()); - } - - return false; - } - - /** - * Get the content of an element - * - * @access public - * @return string/boolean returns false if not available - */ - public function getElementContent() { - $element = $this->getToken(); - - if($element[EN_TYPE] == EN_TYPE_CONTENT) { - return $element[EN_CONTENT]; - } - else { - ZLog::Write(LOGLEVEL_WBXMLSTACK, sprintf("WBXMLDecoder->getElementContent(): unmatched WBXML content: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG]))?$element[EN_TAG]:""), ((isset($element[EN_TYPE]))?$element[EN_TYPE]:""), ((isset($element[EN_FLAGS]))?$element[EN_FLAGS]:""))); - $this->ungetElement($element); - } - - return false; - } - - /** - * 'Ungets' an element writing it into a buffer to be 'get' again - * - * @param element $element the element to get ungetten - * - * @access public - * @return - */ - public function ungetElement($element) { - if($this->ungetbuffer) - ZLog::Write(LOGLEVEL_ERROR,sprintf("WBXMLDecoder->ungetElement(): WBXML double unget on tag: '%s' type '%s' flags '%s'", ((isset($element[EN_TAG]))?$element[EN_TAG]:""), ((isset($element[EN_TYPE]))?$element[EN_TYPE]:""), ((isset($element[EN_FLAGS]))?$element[EN_FLAGS]:""))); - - $this->ungetbuffer = $element; - } - - /** - * Returns the plain input stream - * - * @access public - * @return string - */ - public function GetPlainInputStream() { - return $this->inputBuffer . stream_get_contents($this->in); - } - - /** - * Returns if the input is WBXML - * - * @access public - * @return boolean - */ - public function IsWBXML() { - return $this->isWBXML; - } - - /** - * Reads the remaning data from the input stream - * - * @access public - * @return void - */ - public function readRemainingData() { - ZLog::Write(LOGLEVEL_DEBUG, "WBXMLDecoder->readRemainingData() reading remaining data from input stream"); - while ($this->getElement()); - } - - /** - * Returns the WBXML data read from the stream - * - * @access public - * @return string - base64 encoded wbxml - */ - public function getWBXMLLog() { - $out = ""; - if ($this->inLog) { - $out = base64_encode(stream_get_contents($this->inLog, -1,0)); - } - return $out; - } - - /**---------------------------------------------------------------------------------------------------------- - * Private WBXMLDecoder stuff - */ - - /** - * Returns the next token - * - * @access private - * @return token - */ - private function getToken() { - // See if there's something in the ungetBuffer - if($this->ungetbuffer) { - $element = $this->ungetbuffer; - $this->ungetbuffer = false; - return $element; - } - - $el = $this->_getToken(); - if($this->log) - $this->logToken($el); - - return $el; - } - - /** - * Log the a token to ZLog - * - * @param string $el token - * - * @access private - * @return - */ - private function logToken($el) { - $spaces = str_repeat(" ", count($this->logStack)); - - switch($el[EN_TYPE]) { - case EN_TYPE_STARTTAG: - if($el[EN_FLAGS] & EN_FLAGS_CONTENT) { - ZLog::Write(LOGLEVEL_WBXML,"I " . $spaces . " <". $el[EN_TAG] . ">"); - array_push($this->logStack, $el[EN_TAG]); - } else - ZLog::Write(LOGLEVEL_WBXML,"I " . $spaces . " <" . $el[EN_TAG] . "/>"); - - break; - case EN_TYPE_ENDTAG: - $tag = array_pop($this->logStack); - ZLog::Write(LOGLEVEL_WBXML,"I " . $spaces . ""); - break; - case EN_TYPE_CONTENT: - ZLog::Write(LOGLEVEL_WBXML,"I " . $spaces . " " . $el[EN_CONTENT]); - break; - } - } - - /** - * Returns either a start tag, content or end tag - * - * @access private - * @return - */ - private function _getToken() { - // Get the data from the input stream - $element = array(); - - while(1) { - $byte = fread($this->in, 1); - if($byte === "" || $byte === false) - break; - $byte = ord($byte); - - switch($byte) { - case self::WBXML_SWITCH_PAGE: - $this->tagcp = $this->getByte(); - break; - - case self::WBXML_END: - $element[EN_TYPE] = EN_TYPE_ENDTAG; - return $element; - - case self::WBXML_STR_I: - $element[EN_TYPE] = EN_TYPE_CONTENT; - $element[EN_CONTENT] = $this->getTermStr(); - return $element; - - case self::WBXML_OPAQUE: - $length = $this->getMBUInt(); - $element[EN_TYPE] = EN_TYPE_CONTENT; - $element[EN_CONTENT] = $this->getOpaque($length); - return $element; - - case self::WBXML_ENTITY: - case self::WBXML_LITERAL: - case self::WBXML_EXT_I_0: - case self::WBXML_EXT_I_1: - case self::WBXML_EXT_I_2: - case self::WBXML_PI: - case self::WBXML_LITERAL_C: - case self::WBXML_EXT_T_0: - case self::WBXML_EXT_T_1: - case self::WBXML_EXT_T_2: - case self::WBXML_STR_T: - case self::WBXML_LITERAL_A: - case self::WBXML_EXT_0: - case self::WBXML_EXT_1: - case self::WBXML_EXT_2: - case self::WBXML_LITERAL_AC: - throw new WBXMLException("Invalid token :".$byte); - - default: - if($byte & self::WBXML_WITH_ATTRIBUTES) - throw new WBXMLException("Attributes are not allowed :".$byte); - $element[EN_TYPE] = EN_TYPE_STARTTAG; - $element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3f); - $element[EN_FLAGS] = ($byte & self::WBXML_WITH_CONTENT ? EN_FLAGS_CONTENT : 0); - return $element; - } - } - } - - /** - * Reads from the stream until getting a string terminator - * - * @access private - * @return string - */ - private function getTermStr() { - $str = ""; - while(1) { - $in = fread($this->in, 1); - //this is faster than ord($in) == 0 - if($in === "\0" || $in === false || $in === "") - break; - $str .= $in; - } - - return $str; - } - - /** - * Reads $len from the input stream - * - * @param int $len - * - * @access private - * @return string - */ - private function getOpaque($len) { - $d = stream_get_contents($this->in, $len); - if ($d === false) - throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): stream_get_contents === false", HTTP_CODE_500, null, LOGLEVEL_WARN); - $l = strlen($d); - if ($l !== $len) - throw new HTTPReturnCodeException("WBXMLDecoder->getOpaque(): only $l byte read instead of $len", HTTP_CODE_500, null, LOGLEVEL_WARN); - return $d; - } - - /** - * Reads one byte from the input stream - * - * @access private - * @return int - */ - private function getByte() { - $ch = fread($this->in, 1); - if (strlen($ch) > 0) { - fwrite($this->inLog, $ch); - return ord($ch); - } - else - return; - } - - /** - * Reads string length from the input stream - * - * @access private - * @return - */ - private function getMBUInt() { - $uint = 0; - - while(1) { - $byte = $this->getByte(); - - $uint |= $byte & 0x7f; - - if($byte & 0x80) - $uint = $uint << 7; - else - break; - } - - return $uint; - } - - /** - * Returns the mapping for a specified codepage and id - * - * @param $cp codepage - * @param $id - * - * @access public - * @return string - */ - private function getMapping($cp, $id) { - if(!isset($this->dtd["codes"][$cp]) || !isset($this->dtd["codes"][$cp][$id])) - return false; - else { - if(isset($this->dtd["namespaces"][$cp])) { - return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id]; - } else - return $this->dtd["codes"][$cp][$id]; - } - } -} diff --git a/sources/lib/wbxml/wbxmldefs.php b/sources/lib/wbxml/wbxmldefs.php deleted file mode 100644 index c3a80e0..0000000 --- a/sources/lib/wbxml/wbxmldefs.php +++ /dev/null @@ -1,757 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class WBXMLDefs { - - const WBXML_SWITCH_PAGE = 0x00; - const WBXML_END = 0x01; - const WBXML_ENTITY = 0x02; //not used in ActiveSync - const WBXML_STR_I = 0x03; - const WBXML_LITERAL = 0x04; //not used in ActiveSync - const WBXML_EXT_I_0 = 0x40; //not used in ActiveSync - const WBXML_EXT_I_1 = 0x41; //not used in ActiveSync - const WBXML_EXT_I_2 = 0x42; //not used in ActiveSync - const WBXML_PI = 0x43; //not used in ActiveSync - const WBXML_LITERAL_C = 0x44; //not used in ActiveSync - const WBXML_EXT_T_0 = 0x80; //not used in ActiveSync - const WBXML_EXT_T_1 = 0x81; //not used in ActiveSync - const WBXML_EXT_T_2 = 0x82; //not used in ActiveSync - const WBXML_STR_T = 0x83; //not used in ActiveSync - const WBXML_LITERAL_A = 0x84; //not used in ActiveSync - const WBXML_EXT_0 = 0xC0; //not used in ActiveSync - const WBXML_EXT_1 = 0xC1; //not used in ActiveSync - const WBXML_EXT_2 = 0xC2; //not used in ActiveSync - const WBXML_OPAQUE = 0xC3; - const WBXML_LITERAL_AC = 0xC4; //not used in ActiveSync - - const WBXML_WITH_ATTRIBUTES = 0x80; //not used in ActiveSync - const WBXML_WITH_CONTENT = 0x40; - - /** - * The WBXML DTDs - */ - protected $dtd = array( - "codes" => array ( - 0 => array ( - 0x05 => "Synchronize", - 0x06 => "Replies", //Responses - 0x07 => "Add", - 0x08 => "Modify", //Change - 0x09 => "Remove", //Delete - 0x0a => "Fetch", - 0x0b => "SyncKey", - 0x0c => "ClientEntryId", //ClientId - 0x0d => "ServerEntryId", //ServerId - 0x0e => "Status", - 0x0f => "Folder", //collection - 0x10 => "FolderType", //class - 0x11 => "Version", - 0x12 => "FolderId", //CollectionId - 0x13 => "GetChanges", - 0x14 => "MoreAvailable", - 0x15 => "WindowSize", //WindowSize - MaxItems before z-push 2 - 0x16 => "Perform", //Commands - 0x17 => "Options", - 0x18 => "FilterType", - 0x19 => "Truncation", //2.0 and 2.5 - 0x1a => "RtfTruncation", //2.0 and 2.5 - 0x1b => "Conflict", - 0x1c => "Folders", //Collections - 0x1d => "Data", - 0x1e => "DeletesAsMoves", - 0x1f => "NotifyGUID", //2.0 and 2.5 - 0x20 => "Supported", - 0x21 => "SoftDelete", - 0x22 => "MIMESupport", - 0x23 => "MIMETruncation", - 0x24 => "Wait", //12.1 and 14.0 - 0x25 => "Limit", //12.1 and 14.0 - 0x26 => "Partial", //12.1 and 14.0 - 0x27 => "ConversationMode", //14.0 - 0x28 => "MaxItems", //14.0 - 0x29 => "HeartbeatInterval", //14.0 Either this tag or the Wait tag can be present, but not both. - ), - 1 => array ( - 0x05 => "Anniversary", - 0x06 => "AssistantName", - 0x07 => "AssistnamePhoneNumber", //AssistantTelephoneNumber - 0x08 => "Birthday", - 0x09 => "Body", // 2.5, but is in code page 17 in ActiveSync versions 12.0, 12.1, and 14.0. - 0x0a => "BodySize", //2.0 and 2.5 - 0x0b => "BodyTruncated", //2.0 and 2.5 - 0x0c => "Business2PhoneNumber", - 0x0d => "BusinessCity", - 0x0e => "BusinessCountry", - 0x0f => "BusinessPostalCode", - 0x10 => "BusinessState", - 0x11 => "BusinessStreet", - 0x12 => "BusinessFaxNumber", - 0x13 => "BusinessPhoneNumber", - 0x14 => "CarPhoneNumber", - 0x15 => "Categories", - 0x16 => "Category", - 0x17 => "Children", - 0x18 => "Child", - 0x19 => "CompanyName", - 0x1a => "Department", - 0x1b => "Email1Address", - 0x1c => "Email2Address", - 0x1d => "Email3Address", - 0x1e => "FileAs", - 0x1f => "FirstName", - 0x20 => "Home2PhoneNumber", - 0x21 => "HomeCity", - 0x22 => "HomeCountry", - 0x23 => "HomePostalCode", - 0x24 => "HomeState", - 0x25 => "HomeStreet", - 0x26 => "HomeFaxNumber", - 0x27 => "HomePhoneNumber", - 0x28 => "JobTitle", - 0x29 => "LastName", - 0x2a => "MiddleName", - 0x2b => "MobilePhoneNumber", - 0x2c => "OfficeLocation", - 0x2d => "OtherCity", - 0x2e => "OtherCountry", - 0x2f => "OtherPostalCode", - 0x30 => "OtherState", - 0x31 => "OtherStreet", - 0x32 => "PagerNumber", - 0x33 => "RadioPhoneNumber", - 0x34 => "Spouse", - 0x35 => "Suffix", - 0x36 => "Title", - 0x37 => "WebPage", - 0x38 => "YomiCompanyName", - 0x39 => "YomiFirstName", - 0x3a => "YomiLastName", - 0x3b => "Rtf", //CompressedRTF - 2.5 - 0x3c => "Picture", - 0x3d => "Alias", //14.0 - 0x3e => "WeightedRank" //14.0 - ), - 2 => array ( - 0x05 => "Attachment", //2.5, 12.0, 12.1 and 14.0 - 0x06 => "Attachments", //2.5, 12.0, 12.1 and 14.0 - 0x07 => "AttName", //2.5, 12.0, 12.1 and 14.0 - 0x08 => "AttSize", //2.5, 12.0, 12.1 and 14.0 - 0x09 => "AttOid", //2.5, 12.0, 12.1 and 14.0 - 0x0a => "AttMethod", //2.5, 12.0, 12.1 and 14.0 - 0x0b => "AttRemoved", //2.5, 12.0, 12.1 and 14.0 - 0x0c => "Body", // 2.5, but is in code page 17 in ActiveSync versions 12.0, 12.1, and 14.0. - 0x0d => "BodySize", //2.5, 12.0, 12.1 and 14.0 - 0x0e => "BodyTruncated", //2.5, 12.0, 12.1 and 14.0 - 0x0f => "DateReceived", //2.5, 12.0, 12.1 and 14.0 - 0x10 => "DisplayName", //2.5, 12.0, 12.1 and 14.0 - 0x11 => "DisplayTo", //2.5, 12.0, 12.1 and 14.0 - 0x12 => "Importance", //2.5, 12.0, 12.1 and 14.0 - 0x13 => "MessageClass", //2.5, 12.0, 12.1 and 14.0 - 0x14 => "Subject", //2.5, 12.0, 12.1 and 14.0 - 0x15 => "Read", //2.5, 12.0, 12.1 and 14.0 - 0x16 => "To", //2.5, 12.0, 12.1 and 14.0 - 0x17 => "Cc", //2.5, 12.0, 12.1 and 14.0 - 0x18 => "From", //2.5, 12.0, 12.1 and 14.0 - 0x19 => "Reply-To", //ReplyTo 2.5, 12.0, 12.1 and 14.0 - 0x1a => "AllDayEvent", //2.5, 12.0, 12.1 and 14.0 - 0x1b => "Categories", //2.5, 12.0, 12.1 and 14.0 - 0x1c => "Category", //2.5, 12.0, 12.1 and 14.0 - 0x1d => "DtStamp", //2.5, 12.0, 12.1 and 14.0 - 0x1e => "EndTime", //2.5, 12.0, 12.1 and 14.0 - 0x1f => "InstanceType", //2.5, 12.0, 12.1 and 14.0 - 0x20 => "BusyStatus", //2.5, 12.0, 12.1 and 14.0 - 0x21 => "Location", //2.5, 12.0, 12.1 and 14.0 - 0x22 => "MeetingRequest", //2.5, 12.0, 12.1 and 14.0 - 0x23 => "Organizer", //2.5, 12.0, 12.1 and 14.0 - 0x24 => "RecurrenceId", //2.5, 12.0, 12.1 and 14.0 - 0x25 => "Reminder", //2.5, 12.0, 12.1 and 14.0 - 0x26 => "ResponseRequested", //2.5, 12.0, 12.1 and 14.0 - 0x27 => "Recurrences", //2.5, 12.0, 12.1 and 14.0 - 0x28 => "Recurrence", //2.5, 12.0, 12.1 and 14.0 - 0x29 => "Type", //Recurrence_Type //2.5, 12.0, 12.1 and 14.0 - 0x2a => "Until", //Recurrence_Until //2.5, 12.0, 12.1 and 14.0 - 0x2b => "Occurrences", //Recurrence_Occurrences //2.5, 12.0, 12.1 and 14.0 - 0x2c => "Interval", //Recurrence_Interval //2.5, 12.0, 12.1 and 14.0 - 0x2d => "DayOfWeek", //Recurrence_DayOfWeek //2.5, 12.0, 12.1 and 14.0 - 0x2e => "DayOfMonth", //Recurrence_DayOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x2f => "WeekOfMonth", //Recurrence_WeekOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x30 => "MonthOfYear", //Recurrence_MonthOfYear //2.5, 12.0, 12.1 and 14.0 - 0x31 => "StartTime", //2.5, 12.0, 12.1 and 14.0 - 0x32 => "Sensitivity", //2.5, 12.0, 12.1 and 14.0 - 0x33 => "TimeZone", //2.5, 12.0, 12.1 and 14.0 - 0x34 => "GlobalObjId", //2.5, 12.0, 12.1 and 14.0 - 0x35 => "ThreadTopic", //2.5, 12.0, 12.1 and 14.0 - 0x36 => "MIMEData", //2.5 - 0x37 => "MIMETruncated", //2.5 - 0x38 => "MIMESize", //2.5 - 0x39 => "InternetCPID", //2.5, 12.0, 12.1 and 14.0 - 0x3a => "Flag", //12.0, 12.1 and 14.0 - 0x3b => "FlagStatus", //12.0, 12.1 and 14.0 - 0x3c => "ContentClass", //12.0, 12.1 and 14.0 - 0x3d => "FlagType", //12.0, 12.1 and 14.0 - 0x3e => "CompleteTime", //14.0 - 0x3f => "DisallowNewTimeProposal", //14.0 - ), - 3 => array ( //Code page 3 is no longer in use, however, tokens 05 through 17 have been defined. 20100501 - 0x05 => "Notify", - 0x06 => "Notification", - 0x07 => "Version", - 0x08 => "Lifetime", - 0x09 => "DeviceInfo", - 0x0a => "Enable", - 0x0b => "Folder", - 0x0c => "ServerEntryId", - 0x0d => "DeviceAddress", - 0x0e => "ValidCarrierProfiles", - 0x0f => "CarrierProfile", - 0x10 => "Status", - 0x11 => "Replies", -// 0x05 => "Version='1.1'", - 0x12 => "Devices", - 0x13 => "Device", - 0x14 => "Id", - 0x15 => "Expiry", - 0x16 => "NotifyGUID", - ), - 4 => array ( - 0x05 => "Timezone", //2.5, 12.0, 12.1 and 14.0 - 0x06 => "AllDayEvent", //2.5, 12.0, 12.1 and 14.0 - 0x07 => "Attendees", //2.5, 12.0, 12.1 and 14.0 - 0x08 => "Attendee", //2.5, 12.0, 12.1 and 14.0 - 0x09 => "Email", //Attendee_Email //2.5, 12.0, 12.1 and 14.0 - 0x0a => "Name", //Attendee_Name //2.5, 12.0, 12.1 and 14.0 - 0x0b => "Body", //2.5, but is in code page 17 in ActiveSync versions 12.0, 12.1, and 14.0 - 0x0c => "BodyTruncated", //2.5, 12.0, 12.1 and 14.0 - 0x0d => "BusyStatus", //2.5, 12.0, 12.1 and 14.0 - 0x0e => "Categories", //2.5, 12.0, 12.1 and 14.0 - 0x0f => "Category", //2.5, 12.0, 12.1 and 14.0 - 0x10 => "Rtf", //2.5 - 0x11 => "DtStamp", //2.5, 12.0, 12.1 and 14.0 - 0x12 => "EndTime", //2.5, 12.0, 12.1 and 14.0 - 0x13 => "Exception", //2.5, 12.0, 12.1 and 14.0 - 0x14 => "Exceptions", //2.5, 12.0, 12.1 and 14.0 - 0x15 => "Deleted", //Exception_Deleted //2.5, 12.0, 12.1 and 14.0 - 0x16 => "ExceptionStartTime", //Exception_StartTime //2.5, 12.0, 12.1 and 14.0 - 0x17 => "Location", //2.5, 12.0, 12.1 and 14.0 - 0x18 => "MeetingStatus", //2.5, 12.0, 12.1 and 14.0 - 0x19 => "OrganizerEmail", //Organizer_Email //2.5, 12.0, 12.1 and 14.0 - 0x1a => "OrganizerName", //Organizer_Name //2.5, 12.0, 12.1 and 14.0 - 0x1b => "Recurrence", //2.5, 12.0, 12.1 and 14.0 - 0x1c => "Type", //Recurrence_Type //2.5, 12.0, 12.1 and 14.0 - 0x1d => "Until", //Recurrence_Until //2.5, 12.0, 12.1 and 14.0 - 0x1e => "Occurrences", //Recurrence_Occurrences //2.5, 12.0, 12.1 and 14.0 - 0x1f => "Interval", //Recurrence_Interval //2.5, 12.0, 12.1 and 14.0 - 0x20 => "DayOfWeek", //Recurrence_DayOfWeek //2.5, 12.0, 12.1 and 14.0 - 0x21 => "DayOfMonth", //Recurrence_DayOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x22 => "WeekOfMonth", //Recurrence_WeekOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x23 => "MonthOfYear", //Recurrence_MonthOfYear //2.5, 12.0, 12.1 and 14.0 - 0x24 => "Reminder", //Reminder_MinsBefore //2.5, 12.0, 12.1 and 14.0 - 0x25 => "Sensitivity", //2.5, 12.0, 12.1 and 14.0 - 0x26 => "Subject", //2.5, 12.0, 12.1 and 14.0 - 0x27 => "StartTime", //2.5, 12.0, 12.1 and 14.0 - 0x28 => "UID", //2.5, 12.0, 12.1 and 14.0 - 0x29 => "Attendee_Status", //12.0, 12.1 and 14.0 - 0x2a => "Attendee_Type", //12.0, 12.1 and 14.0 - 0x2b => "Attachment", //12.0, 12.1 and 14.0 - 0x2c => "Attachments", //12.0, 12.1 and 14.0 - 0x2d => "AttName", //12.0, 12.1 and 14.0 - 0x2e => "AttSize", //12.0, 12.1 and 14.0 - 0x2f => "AttOid", //12.0, 12.1 and 14.0 - 0x30 => "AttMethod", //12.0, 12.1 and 14.0 - 0x31 => "AttRemoved", //12.0, 12.1 and 14.0 - 0x32 => "DisplayName", //12.0, 12.1 and 14.0 - 0x33 => "DisallowNewTimeProposal", //14.0 - 0x34 => "ResponseRequested", //14.0 - 0x35 => "AppointmentReplyTime", //14.0 - 0x36 => "ResponseType", //14.0 - 0x37 => "CalendarType", //14.0 - 0x38 => "IsLeapMonth", //14.0 - 0x39 => "FirstDayOfWeek", //post 14.0 20100501 - 0x3a => "OnlineMeetingInternalLink", //post 14.0 20100501 - 0x3b => "OnlineMeetingExternalLink", //post 14.0 20120630 - ), - 5 => array ( - 0x05 => "Moves", - 0x06 => "Move", - 0x07 => "SrcMsgId", - 0x08 => "SrcFldId", - 0x09 => "DstFldId", - 0x0a => "Response", - 0x0b => "Status", - 0x0c => "DstMsgId", - ), - 6 => array ( - 0x05 => "GetItemEstimate", - 0x06 => "Version", //only 12.1 20100501 - 0x07 => "Folders", //Collections - 0x08 => "Folder", //Collection - 0x09 => "FolderType", //Class //only 12.1 //The tag defined in code page 0 should be used in all other instances. 20100501 - 0x0a => "FolderId", //CollectionId - 0x0b => "DateTime", //not supported by 14. only supported 12.1. 20100501 - 0x0c => "Estimate", - 0x0d => "Response", - 0x0e => "Status", - ), - 7 => array ( - 0x05 => "Folders", //2.0 - 0x06 => "Folder", //2.0 - 0x07 => "DisplayName", - 0x08 => "ServerEntryId", //ServerId - 0x09 => "ParentId", - 0x0a => "Type", - 0x0b => "Response", //2.0 - 0x0c => "Status", - 0x0d => "ContentClass", //2.0 - 0x0e => "Changes", - 0x0f => "Add", - 0x10 => "Remove", - 0x11 => "Update", - 0x12 => "SyncKey", - 0x13 => "FolderCreate", - 0x14 => "FolderDelete", - 0x15 => "FolderUpdate", - 0x16 => "FolderSync", - 0x17 => "Count", - 0x18 => "Version", //2.0 - not defined in 20100501 - ), - 8 => array ( - 0x05 => "CalendarId", - 0x06 => "FolderId", //CollectionId - 0x07 => "MeetingResponse", - 0x08 => "RequestId", - 0x09 => "Request", - 0x0a => "Result", - 0x0b => "Status", - 0x0c => "UserResponse", - 0x0d => "Version", //2.0 - not defined in 20100501 - 0x0e => "InstanceId" // first in 20100501 - ), - 9 => array ( - 0x05 => "Body", //2.5, but is in code page 17 in ActiveSync versions 12.0, 12.1, and 14.0 - 0x06 => "BodySize", //2.5, but is in code page 17 as the EstimatedDataSize tag in ActiveSync versions 12.0, 12.1 and 14.0 - 0x07 => "BodyTruncated", //2.5, but is in code page 17 as the Truncated tag in ActiveSync versions 12.0, 12.1, and 14.0 - 0x08 => "Categories", //2.5, 12.0, 12.1 and 14.0 - 0x09 => "Category", //2.5, 12.0, 12.1 and 14.0 - 0x0a => "Complete", //2.5, 12.0, 12.1 and 14.0 - 0x0b => "DateCompleted", //2.5, 12.0, 12.1 and 14.0 - 0x0c => "DueDate", //2.5, 12.0, 12.1 and 14.0 - 0x0d => "UtcDueDate", //2.5, 12.0, 12.1 and 14.0 - 0x0e => "Importance", //2.5, 12.0, 12.1 and 14.0 - 0x0f => "Recurrence", //2.5, 12.0, 12.1 and 14.0 - 0x10 => "Type", //Recurrence_Type //2.5, 12.0, 12.1 and 14.0 - 0x11 => "Start", //Recurrence_Start //2.5, 12.0, 12.1 and 14.0 - 0x12 => "Until", //Recurrence_Until //2.5, 12.0, 12.1 and 14.0 - 0x13 => "Occurrences", //Recurrence_Occurrences //2.5, 12.0, 12.1 and 14.0 - 0x14 => "Interval", //Recurrence_Interval //2.5, 12.0, 12.1 and 14.0 - 0x16 => "DayOfWeek", //Recurrence_DayOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x15 => "DayOfMonth", //Recurrence_DayOfWeek //2.5, 12.0, 12.1 and 14.0 - 0x17 => "WeekOfMonth", //Recurrence_WeekOfMonth //2.5, 12.0, 12.1 and 14.0 - 0x18 => "MonthOfYear", //Recurrence_MonthOfYear //2.5, 12.0, 12.1 and 14.0 - 0x19 => "Regenerate", //Recurrence_Regenerate //2.5, 12.0, 12.1 and 14.0 - 0x1a => "DeadOccur", //Recurrence_DeadOccur //2.5, 12.0, 12.1 and 14.0 - 0x1b => "ReminderSet", //2.5, 12.0, 12.1 and 14.0 - 0x1c => "ReminderTime", //2.5, 12.0, 12.1 and 14.0 - 0x1d => "Sensitivity", //2.5, 12.0, 12.1 and 14.0 - 0x1e => "StartDate", //2.5, 12.0, 12.1 and 14.0 - 0x1f => "UtcStartDate", //2.5, 12.0, 12.1 and 14.0 - 0x20 => "Subject", //2.5, 12.0, 12.1 and 14.0 - 0x21 => "Rtf", //CompressedRTF //2.5, but is in code page 17 as the Type tag in Active Sync versions 12.0, 12.1, and 14.0 - 0x22 => "OrdinalDate", //12.0, 12.1 and 14.0 - 0x23 => "SubOrdinalDate", //12.0, 12.1 and 14.0 - 0x24 => "CalendarType", //14.0 - 0x25 => "IsLeapMonth", //14.0 - 0x26 => "FirstDayOfWeek", // first in 20100501 post 14.0 - ), - 0xa => array ( - 0x05 => "ResolveRecipients", - 0x06 => "Response", - 0x07 => "Status", - 0x08 => "Type", - 0x09 => "Recipient", - 0x0a => "DisplayName", - 0x0b => "EmailAddress", - 0x0c => "Certificates", - 0x0d => "Certificate", - 0x0e => "MiniCertificate", - 0x0f => "Options", - 0x10 => "To", - 0x11 => "CertificateRetrieval", - 0x12 => "RecipientCount", - 0x13 => "MaxCertificates", - 0x14 => "MaxAmbiguousRecipients", - 0x15 => "CertificateCount", - 0x16 => "Availability", //14.0 - 0x17 => "StartTime", //14.0 - 0x18 => "EndTime", //14.0 - 0x19 => "MergedFreeBusy", //14.0 - 0x1A => "Picture", // first in 20100501 post 14.0 - 0x1B => "MaxSize", // first in 20100501 post 14.0 - 0x1C => "Data", // first in 20100501 post 14.0 - 0x1D => "MaxPictures", // first in 20100501 post 14.0 - ), - 0xb => array ( - 0x05 => "ValidateCert", - 0x06 => "Certificates", - 0x07 => "Certificate", - 0x08 => "CertificateChain", - 0x09 => "CheckCRL", - 0x0a => "Status", - ), - 0xc => array ( - 0x05 => "CustomerId", - 0x06 => "GovernmentId", - 0x07 => "IMAddress", - 0x08 => "IMAddress2", - 0x09 => "IMAddress3", - 0x0a => "ManagerName", - 0x0b => "CompanyMainPhone", - 0x0c => "AccountName", - 0x0d => "NickName", - 0x0e => "MMS", - ), - 0xd => array ( - 0x05 => "Ping", - 0x06 => "AutdState", //(Not used by protocol) - 0x07 => "Status", - 0x08 => "LifeTime", //HeartbeatInterval - 0x09 => "Folders", - 0x0a => "Folder", - 0x0b => "ServerEntryId", //Id - 0x0c => "FolderType", //Class - 0x0d => "MaxFolders", - 0x0e => "Version" //not defined in 20100501 - ), - 0xe => array ( - 0x05 => "Provision", //2.5, 12.0, 12.1 and 14.0 - 0x06 => "Policies", //2.5, 12.0, 12.1 and 14.0 - 0x07 => "Policy", //2.5, 12.0, 12.1 and 14.0 - 0x08 => "PolicyType", //2.5, 12.0, 12.1 and 14.0 - 0x09 => "PolicyKey", //2.5, 12.0, 12.1 and 14.0 - 0x0A => "Data", //2.5, 12.0, 12.1 and 14.0 - 0x0B => "Status", //2.5, 12.0, 12.1 and 14.0 - 0x0C => "RemoteWipe", //2.5, 12.0, 12.1 and 14.0 - 0x0D => "EASProvisionDoc", //12.0, 12.1 and 14.0 - 0x0E => "DevicePasswordEnabled", //12.0, 12.1 and 14.0 - 0x0F => "AlphanumericDevicePasswordRequired", //12.0, 12.1 and 14.0 - 0x10 => "DeviceEncryptionEnabled", //12.0, 12.1 and 14.0 - //0x10 => "RequireStorageCardEncryption", //12.1 and 14.0 - 0x11 => "PasswordRecoveryEnabled", //12.0, 12.1 and 14.0 - 0x12 => "DocumentBrowseEnabled", //2.0 and 2.5. - 0x13 => "AttachmentsEnabled", //12.0, 12.1 and 14.0 - 0x14 => "MinDevicePasswordLength", //12.0, 12.1 and 14.0 - 0x15 => "MaxInactivityTimeDeviceLock", //12.0, 12.1 and 14.0 - 0x16 => "MaxDevicePasswordFailedAttempts", //12.0, 12.1 and 14.0 - 0x17 => "MaxAttachmentSize", //12.0, 12.1 and 14.0 - 0x18 => "AllowSimpleDevicePassword", //12.0, 12.1 and 14.0 - 0x19 => "DevicePasswordExpiration", //12.0, 12.1 and 14.0 - 0x1A => "DevicePasswordHistory", //12.0, 12.1 and 14.0 - 0x1B => "AllowStorageCard", //12.1 and 14.0 - 0x1C => "AllowCamera", //12.1 and 14.0 - 0x1D => "RequireDeviceEncryption", //12.1 and 14.0 - 0x1E => "AllowUnsignedApplications", //12.1 and 14.0 - 0x1F => "AllowUnsignedInstallationPackages", //12.1 and 14.0 - 0x20 => "MinDevicePasswordComplexCharacters", //12.1 and 14.0 - 0x21 => "AllowWiFi", //12.1 and 14.0 - 0x22 => "AllowTextMessaging", //12.1 and 14.0 - 0x23 => "AllowPOPIMAPEmail", //12.1 and 14.0 - 0x24 => "AllowBluetooth", //12.1 and 14.0 - 0x25 => "AllowIrDA", //12.1 and 14.0 - 0x26 => "RequireManualSyncWhenRoaming", //12.1 and 14.0 - 0x27 => "AllowDesktopSync", //12.1 and 14.0 - 0x28 => "MaxCalendarAgeFilter", //12.1 and 14.0 - 0x29 => "AllowHTMLEmail", //12.1 and 14.0 - 0x2A => "MaxEmailAgeFilter", //12.1 and 14.0 - 0x2B => "MaxEmailBodyTruncationSize", //12.1 and 14.0 - 0x2C => "MaxEmailHTMLBodyTruncationSize", //12.1 and 14.0 - 0x2D => "RequireSignedSMIMEMessages", //12.1 and 14.0 - 0x2E => "RequireEncryptedSMIMEMessages", //12.1 and 14.0 - 0x2F => "RequireSignedSMIMEAlgorithm", //12.1 and 14.0 - 0x30 => "RequireEncryptionSMIMEAlgorithm", //12.1 and 14.0 - 0x31 => "AllowSMIMEEncryptionAlgorithmNegotiation", //12.1 and 14.0 - 0x32 => "AllowSMIMESoftCerts", //12.1 and 14.0 - 0x33 => "AllowBrowser", //12.1 and 14.0 - 0x34 => "AllowConsumerEmail", //12.1 and 14.0 - 0x35 => "AllowRemoteDesktop", //12.1 and 14.0 - 0x36 => "AllowInternetSharing", //12.1 and 14.0 - 0x37 => "UnapprovedInROMApplicationList", //12.1 and 14.0 - 0x38 => "ApplicationName", //12.1 and 14.0 - 0x39 => "ApprovedApplicationList", //12.1 and 14.0 - 0x3A => "Hash", //12.1 and 14.0 - ), - 0xf => array( - 0x05 => "Search", //12.0, 12.1 and 14.0 - 0x07 => "Store", //12.0, 12.1 and 14.0 - 0x08 => "Name", //12.0, 12.1 and 14.0 - 0x09 => "Query", //12.0, 12.1 and 14.0 - 0x0A => "Options", //12.0, 12.1 and 14.0 - 0x0B => "Range", //12.0, 12.1 and 14.0 - 0x0C => "Status", //12.0, 12.1 and 14.0 - 0x0D => "Response", //12.0, 12.1 and 14.0 - 0x0E => "Result", //12.0, 12.1 and 14.0 - 0x0F => "Properties", //12.0, 12.1 and 14.0 - 0x10 => "Total", //12.0, 12.1 and 14.0 - 0x11 => "EqualTo", //12.0, 12.1 and 14.0 - 0x12 => "Value", //12.0, 12.1 and 14.0 - 0x13 => "And", //12.0, 12.1 and 14.0 - 0x14 => "Or", //14.0 - 0x15 => "FreeText", //12.0, 12.1 and 14.0 - 0x17 => "DeepTraversal", //12.0, 12.1 and 14.0 - 0x18 => "LongId", //12.0, 12.1 and 14.0 - 0x19 => "RebuildResults", //12.0, 12.1 and 14.0 - 0x1A => "LessThan", //12.0, 12.1 and 14.0 - 0x1B => "GreaterThan", //12.0, 12.1 and 14.0 - 0x1C => "Schema", //12.0, 12.1 and 14.0 - 0x1D => "Supported", //12.0, 12.1 and 14.0 - 0x1E => "UserName", //12.1 and 14.0 - 0x1F => "Password", //12.1 and 14.0 - 0x20 => "ConversationId", //14.0 - 0x21 => "Picture", // first in 20100501 post 14.0 - 0x22 => "MaxSize", // first in 20100501 post 14.0 - 0x23 => "MaxPictures", // first in 20100501 post 14.0 - ), - 0x10 => array( - 0x05 => "DisplayName", - 0x06 => "Phone", - 0x07 => "Office", - 0x08 => "Title", - 0x09 => "Company", - 0x0A => "Alias", - 0x0B => "FirstName", - 0x0C => "LastName", - 0x0D => "HomePhone", - 0x0E => "MobilePhone", - 0x0F => "EmailAddress", - 0x10 => "Picture", // first in 20100501 post 14.0 - 0x11 => "Status", // first in 20100501 post 14.0 - 0x12 => "Data", // first in 20100501 post 14.0 - ), - 0x11 => array( //12.0, 12.1 and 14.0 - 0x05 => "BodyPreference", - 0x06 => "Type", - 0x07 => "TruncationSize", - 0x08 => "AllOrNone", - 0x0A => "Body", - 0x0B => "Data", - 0x0C => "EstimatedDataSize", - 0x0D => "Truncated", - 0x0E => "Attachments", - 0x0F => "Attachment", - 0x10 => "DisplayName", - 0x11 => "FileReference", - 0x12 => "Method", - 0x13 => "ContentId", - 0x14 => "ContentLocation", //not used - 0x15 => "IsInline", - 0x16 => "NativeBodyType", - 0x17 => "ContentType", - 0x18 => "Preview", //14.0 - 0x19 => "BodyPartPreference", // first in 20100501 post 14.0 - 0x1A => "BodyPart", // first in 20100501 post 14.0 - 0x1B => "Status", // first in 20100501 post 14.0 - ), - 0x12 => array( //12.0, 12.1 and 14.0 - 0x05 => "Settings", //12.0, 12.1 and 14.0 - 0x06 => "Status", //12.0, 12.1 and 14.0 - 0x07 => "Get", //12.0, 12.1 and 14.0 - 0x08 => "Set", //12.0, 12.1 and 14.0 - 0x09 => "Oof", //12.0, 12.1 and 14.0 - 0x0A => "OofState", //12.0, 12.1 and 14.0 - 0x0B => "StartTime", //12.0, 12.1 and 14.0 - 0x0C => "EndTime", //12.0, 12.1 and 14.0 - 0x0D => "OofMessage", //12.0, 12.1 and 14.0 - 0x0E => "AppliesToInternal", //12.0, 12.1 and 14.0 - 0x0F => "AppliesToExternalKnown", //12.0, 12.1 and 14.0 - 0x10 => "AppliesToExternalUnknown", //12.0, 12.1 and 14.0 - 0x11 => "Enabled", //12.0, 12.1 and 14.0 - 0x12 => "ReplyMessage", //12.0, 12.1 and 14.0 - 0x13 => "BodyType", //12.0, 12.1 and 14.0 - 0x14 => "DevicePassword", //12.0, 12.1 and 14.0 - 0x15 => "Password", //12.0, 12.1 and 14.0 - 0x16 => "DeviceInformaton", //12.0, 12.1 and 14.0 - 0x17 => "Model", //12.0, 12.1 and 14.0 - 0x18 => "IMEI", //12.0, 12.1 and 14.0 - 0x19 => "FriendlyName", //12.0, 12.1 and 14.0 - 0x1A => "OS", //12.0, 12.1 and 14.0 - 0x1B => "OSLanguage", //12.0, 12.1 and 14.0 - 0x1C => "PhoneNumber", //12.0, 12.1 and 14.0 - 0x1D => "UserInformation", //12.0, 12.1 and 14.0 - 0x1E => "EmailAddresses", //12.0, 12.1 and 14.0 - 0x1F => "SmtpAddress", //12.0, 12.1 and 14.0 - 0x20 => "UserAgent", //12.1 and 14.0 - 0x21 => "EnableOutboundSMS", //14.0 - 0x22 => "MobileOperator", //14.0 - 0x23 => "PrimarySmtpAddress", // first in 20100501 post 14.0 - 0x24 => "Accounts", // first in 20100501 post 14.0 - 0x25 => "Account", // first in 20100501 post 14.0 - 0x26 => "AccountId", // first in 20100501 post 14.0 - 0x27 => "AccountName", // first in 20100501 post 14.0 - 0x28 => "UserDisplayName", // first in 20100501 post 14.0 - 0x29 => "SendDisabled", // first in 20100501 post 14.0 - 0x2B => "ihsManagementInformation", // first in 20100501 post 14.0 - ), - 0x13 => array( //12.0, 12.1 and 14.0 - 0x05 => "LinkId", - 0x06 => "DisplayName", - 0x07 => "IsFolder", - 0x08 => "CreationDate", - 0x09 => "LastModifiedDate", - 0x0A => "IsHidden", - 0x0B => "ContentLength", - 0x0C => "ContentType", - ), - 0x14 => array( //12.0, 12.1 and 14.0 - 0x05 => "ItemOperations", - 0x06 => "Fetch", - 0x07 => "Store", - 0x08 => "Options", - 0x09 => "Range", - 0x0A => "Total", - 0x0B => "Properties", - 0x0C => "Data", - 0x0D => "Status", - 0x0E => "Response", - 0x0F => "Version", - 0x10 => "Schema", - 0x11 => "Part", - 0x12 => "EmptyFolderContents", - 0x13 => "DeleteSubFolders", - 0x14 => "UserName", //12.1 and 14.0 - 0x15 => "Password", //12.1 and 14.0 - 0x16 => "Move", //14.0 - 0x17 => "DstFldId", //14.0 - 0x18 => "ConversationId", //14.0 - 0x19 => "MoveAlways", //14.0 - ), - 0x15 => array( //14.0 - 0x05 => "SendMail", - 0x06 => "SmartForward", - 0x07 => "SmartReply", - 0x08 => "SaveInSentItems", - 0x09 => "ReplaceMime", - 0x0A => "Type", - 0x0B => "Source", - 0x0C => "FolderId", - 0x0D => "ItemId", - 0x0E => "LongId", - 0x0F => "InstanceId", - 0x10 => "MIME", - 0x11 => "ClientId", - 0x12 => "Status", - 0x13 => "AccountId", // first in 20100501 post 14.0 - ), - 0x16 => array( // 14.0 - 0x05 => "UmCallerId", - 0x06 => "UmUserNotes", - 0x07 => "UmAttDuration", - 0x08 => "UmAttOrder", - 0x09 => "ConversationId", - 0x0A => "ConversationIndex", - 0x0B => "LastVerbExecuted", - 0x0C => "LastVerbExecutionTime", - 0x0D => "ReceivedAsBcc", - 0x0E => "Sender", - 0x0F => "CalendarType", - 0x10 => "IsLeapMonth", - 0x11 => "AccountId", // first in 20100501 post 14.0 - 0x12 => "FirstDayOfWeek", // first in 20100501 post 14.0 - 0x13 => "MeetingMessageType", // first in 20100501 post 14.0 - ), - 0x17 => array( //14.0 - 0x05 => "Subject", - 0x06 => "MessageClass", - 0x07 => "LastModifiedDate", - 0x08 => "Categories", - 0x09 => "Category", - ), - 0x18 => array( // post 14.0 - 0x05 => "RightsManagementSupport", - 0x06 => "RightsManagementTemplates", - 0x07 => "RightsManagementTemplate", - 0x08 => "RightsManagementLicense", - 0x09 => "EditAllowed", - 0x0A => "ReplyAllowed", - 0x0B => "ReplyAllAllowed", - 0x0C => "ForwardAllowed", - 0x0D => "ModifyRecipientsAllowed", - 0x0E => "ExtractAllowed", - 0x0F => "PrintAllowed", - 0x10 => "ExportAllowed", - 0x11 => "ProgrammaticAccessAllowed", - 0x12 => "RMOwner", - 0x13 => "ContentExpiryDate", - 0x14 => "TemplateID", - 0x15 => "TemplateName", - 0x16 => "TemplateDescription", - 0x17 => "ContentOwner", - 0x18 => "RemoveRightsManagementDistribution", - ), - ), - "namespaces" => array( - //0 => "AirSync", // - 1 => "POOMCONTACTS", - 2 => "POOMMAIL", - 3 => "AirNotify", //no longer used - 4 => "POOMCAL", - 5 => "Move", - 6 => "GetItemEstimate", - 7 => "FolderHierarchy", - 8 => "MeetingResponse", - 9 => "POOMTASKS", - 0xA => "ResolveRecipients", - 0xB => "ValidateCert", - 0xC => "POOMCONTACTS2", - 0xD => "Ping", - 0xE => "Provision",// - 0xF => "Search",// - 0x10 => "GAL", - 0x11 => "AirSyncBase", //12.0, 12.1 and 14.0 - 0x12 => "Settings", //12.0, 12.1 and 14.0. - 0x13 => "DocumentLibrary", //12.0, 12.1 and 14.0 - 0x14 => "ItemOperations", //12.0, 12.1 and 14.0 - 0x15 => "ComposeMail", //14.0 - 0x16 => "POOMMAIL2", //14.0 - 0x17 => "Notes", //14.0 - 0x18 => "RightsManagement", - ) - ); -} diff --git a/sources/lib/wbxml/wbxmlencoder.php b/sources/lib/wbxml/wbxmlencoder.php deleted file mode 100644 index 07e4ad5..0000000 --- a/sources/lib/wbxml/wbxmlencoder.php +++ /dev/null @@ -1,513 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -class WBXMLEncoder extends WBXMLDefs { - private $_dtd; - private $_out; - private $_outLog; - - private $_tagcp = 0; - - private $log = false; - private $logStack = array(); - - // We use a delayed output mechanism in which we only output a tag when it actually has something - // in it. This can cause entire XML trees to disappear if they don't have output data in them; Ie - // calling 'startTag' 10 times, and then 'endTag' will cause 0 bytes of output apart from the header. - - // Only when content() is called do we output the current stack of tags - - private $_stack; - - private $multipart; // the content is multipart - private $bodyparts; - - public function WBXMLEncoder($output, $multipart = false) { - $this->log = defined('WBXML_DEBUG') && WBXML_DEBUG; - - $this->_out = $output; - $this->_outLog = StringStreamWrapper::Open(""); - - // reverse-map the DTD - foreach($this->dtd["namespaces"] as $nsid => $nsname) { - $this->_dtd["namespaces"][$nsname] = $nsid; - } - - foreach($this->dtd["codes"] as $cp => $value) { - $this->_dtd["codes"][$cp] = array(); - foreach($this->dtd["codes"][$cp] as $tagid => $tagname) { - $this->_dtd["codes"][$cp][$tagname] = $tagid; - } - } - $this->_stack = array(); - $this->multipart = $multipart; - $this->bodyparts = array(); - } - - /** - * Puts the WBXML header on the stream - * - * @access public - * @return - */ - public function startWBXML() { - if ($this->multipart) { - header("Content-Type: application/vnd.ms-sync.multipart"); - ZLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.multipart"); - } - else { - header("Content-Type: application/vnd.ms-sync.wbxml"); - ZLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->startWBXML() type: vnd.ms-sync.wbxml"); - } - - $this->outByte(0x03); // WBXML 1.3 - $this->outMBUInt(0x01); // Public ID 1 - $this->outMBUInt(106); // UTF-8 - $this->outMBUInt(0x00); // string table length (0) - } - - /** - * Puts a StartTag on the output stack - * - * @param $tag - * @param $attributes - * @param $nocontent - * - * @access public - * @return - */ - public function startTag($tag, $attributes = false, $nocontent = false) { - $stackelem = array(); - - if(!$nocontent) { - $stackelem['tag'] = $tag; - $stackelem['nocontent'] = $nocontent; - $stackelem['sent'] = false; - - array_push($this->_stack, $stackelem); - - // If 'nocontent' is specified, then apparently the user wants to force - // output of an empty tag, and we therefore output the stack here - } else { - $this->_outputStack(); - $this->_startTag($tag, $nocontent); - } - } - - /** - * Puts an EndTag on the stack - * - * @access public - * @return - */ - public function endTag() { - $stackelem = array_pop($this->_stack); - - // Only output end tags for items that have had a start tag sent - if($stackelem['sent']) { - $this->_endTag(); - - if(count($this->_stack) == 0) - ZLog::Write(LOGLEVEL_DEBUG, "WBXMLEncoder->endTag() WBXML output completed"); - - if(count($this->_stack) == 0 && $this->multipart == true) { - $this->processMultipart(); - } - if(count($this->_stack) == 0) - $this->writeLog(); - } - } - - /** - * Puts content on the output stack - * - * @param $content - * - * @access public - * @return string - */ - public function content($content) { - // We need to filter out any \0 chars because it's the string terminator in WBXML. We currently - // cannot send \0 characters within the XML content anywhere. - $content = str_replace("\0","",$content); - - if("x" . $content == "x") - return; - $this->_outputStack(); - $this->_content($content); - } - - /** - * Gets the value of multipart - * - * @access public - * @return boolean - */ - public function getMultipart() { - return $this->multipart; - } - - /** - * Adds a bodypart - * - * @param Stream $bp - * - * @access public - * @return void - */ - public function addBodypartStream($bp) { - if (!is_resource($bp)) - throw new Exception("WBXMLEncoder->addBodypartStream(): trying to add a ".gettype($bp)." instead off a stream"); - if ($this->multipart) - $this->bodyparts[] = $bp; - } - - /** - * Gets the number of bodyparts - * - * @access public - * @return int - */ - public function getBodypartsCount() { - return count($this->bodyparts); - } - - /**---------------------------------------------------------------------------------------------------------- - * Private WBXMLEncoder stuff - */ - - /** - * Output any tags on the stack that haven't been output yet - * - * @access private - * @return - */ - private function _outputStack() { - for($i=0;$i_stack);$i++) { - if(!$this->_stack[$i]['sent']) { - $this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['nocontent']); - $this->_stack[$i]['sent'] = true; - } - } - } - - /** - * Outputs an actual start tag - * - * @access private - * @return - */ - private function _startTag($tag, $nocontent = false) { - if ($this->log) - $this->logStartTag($tag, $nocontent); - - $mapping = $this->getMapping($tag); - - if(!$mapping) - return false; - - if($this->_tagcp != $mapping["cp"]) { - $this->outSwitchPage($mapping["cp"]); - $this->_tagcp = $mapping["cp"]; - } - - $code = $mapping["code"]; - - if(!isset($nocontent) || !$nocontent) - $code |= 0x40; - - $this->outByte($code); - } - - /** - * Outputs actual data - * - * @access private - * @return - */ - private function _content($content) { - if ($this->log) - $this->logContent($content); - $this->outByte(self::WBXML_STR_I); - $this->outTermStr($content); - } - - /** - * Outputs an actual end tag - * - * @access private - * @return - */ - private function _endTag() { - if ($this->log) - $this->logEndTag(); - $this->outByte(self::WBXML_END); - } - - /** - * Outputs a byte - * - * @param $byte - * - * @access private - * @return - */ - private function outByte($byte) { - fwrite($this->_out, chr($byte)); - fwrite($this->_outLog, chr($byte)); - } - - /** - * Outputs a string table - * - * @param $uint - * - * @access private - * @return - */ - private function outMBUInt($uint) { - while(1) { - $byte = $uint & 0x7f; - $uint = $uint >> 7; - if($uint == 0) { - $this->outByte($byte); - break; - } else { - $this->outByte($byte | 0x80); - } - } - } - - /** - * Outputs content with string terminator - * - * @param $content - * - * @access private - * @return - */ - private function outTermStr($content) { - fwrite($this->_out, $content); - fwrite($this->_out, chr(0)); - fwrite($this->_outLog, $content); - fwrite($this->_outLog, chr(0)); - } - - /** - * Switches the codepage - * - * @param $page - * - * @access private - * @return - */ - private function outSwitchPage($page) { - $this->outByte(self::WBXML_SWITCH_PAGE); - $this->outByte($page); - } - - /** - * Get the mapping for a tag - * - * @param $tag - * - * @access private - * @return array - */ - private function getMapping($tag) { - $mapping = array(); - - $split = $this->splitTag($tag); - - if(isset($split["ns"])) { - $cp = $this->_dtd["namespaces"][$split["ns"]]; - } - else { - $cp = 0; - } - - $code = $this->_dtd["codes"][$cp][$split["tag"]]; - - $mapping["cp"] = $cp; - $mapping["code"] = $code; - - return $mapping; - } - - /** - * Split a tag from a the fulltag (namespace + tag) - * - * @param $fulltag - * - * @access private - * @return array keys: 'ns' (namespace), 'tag' (tag) - */ - private function splitTag($fulltag) { - $ns = false; - $pos = strpos($fulltag, chr(58)); // chr(58) == ':' - - if($pos) { - $ns = substr($fulltag, 0, $pos); - $tag = substr($fulltag, $pos+1); - } - else { - $tag = $fulltag; - } - - $ret = array(); - if($ns) - $ret["ns"] = $ns; - $ret["tag"] = $tag; - - return $ret; - } - - /** - * Logs a StartTag to ZLog - * - * @param $tag - * @param $nocontent - * - * @access private - * @return - */ - private function logStartTag($tag, $nocontent) { - $spaces = str_repeat(" ", count($this->logStack)); - if($nocontent) - ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . " <$tag/>"); - else { - array_push($this->logStack, $tag); - ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . " <$tag>"); - } - } - - /** - * Logs a EndTag to ZLog - * - * @access private - * @return - */ - private function logEndTag() { - $spaces = str_repeat(" ", count($this->logStack)); - $tag = array_pop($this->logStack); - ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . ""); - } - - /** - * Logs content to ZLog - * - * @param $content - * - * @access private - * @return - */ - private function logContent($content) { - $spaces = str_repeat(" ", count($this->logStack)); - ZLog::Write(LOGLEVEL_WBXML,"O " . $spaces . $content); - } - - /** - * Processes the multipart response - * - * @access private - * @return void - */ - private function processMultipart() { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("WBXMLEncoder->processMultipart() with %d parts to be processed", $this->getBodypartsCount())); - $len = ob_get_length(); - // #190 - KD 2015-06-08 Replace ob_get_flush with ob_get_clean; because we don't want to disable the buffering - $buffer = ob_get_clean(); - $nrBodyparts = $this->getBodypartsCount(); - $blockstart = (($nrBodyparts + 1) * 2) * 4 + 4; - - $data = pack("iii", ($nrBodyparts + 1), $blockstart, $len); - - foreach ($this->bodyparts as $bp) { - $blockstart = $blockstart + $len; - if (is_resource($bp)) { - $len = fstat($bp); - $len = (isset($len['size'])) ? $len['size'] : 0; - } elseif (is_string($bp)) { - $len = strlen($bp); - } else { - throw new Exception("bp is a ".gettype($bp)."!?!"); - } - $data .= pack("ii", $blockstart, $len); - } - - fwrite($this->_out, $data); - fwrite($this->_out, $buffer); - fwrite($this->_outLog, $data); - fwrite($this->_outLog, $buffer); - foreach($this->bodyparts as $bp) { - if (is_resource($bp)) { - stream_copy_to_stream($bp, $this->_out); - stream_copy_to_stream($bp, $this->_outLog); - fclose($bp); - } elseif (is_string($bp)) { - fwrite($this->_out, $bp); - fwrite($this->_outLog, $bp); - } else { - throw new Exception("bp is a ".gettype($bp)."!?!"); - } - } - } - - /** - * Writes the sent WBXML data to the log if it is not bigger than 512K. - * - * @access private - * @return void - */ - private function writeLog() { - $stat = fstat($this->_outLog); - if ($stat['size'] < 524288) { - $data = base64_encode(stream_get_contents($this->_outLog, -1,0)); - } - else { - $data = "more than 512K of data"; - } - ZLog::Write(LOGLEVEL_WBXML, "WBXML-OUT: ". $data, false); - } -} diff --git a/sources/lib/webservice/webservice.php b/sources/lib/webservice/webservice.php deleted file mode 100644 index fc4dd5e..0000000 --- a/sources/lib/webservice/webservice.php +++ /dev/null @@ -1,91 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class Webservice { - private $server; - - /** - * Handles a webservice command - * - * @param int $commandCode - * - * @access public - * @return boolean - * @throws SoapFault - */ - public function Handle($commandCode) { - if (Request::GetDeviceType() !== "webservice" || Request::GetDeviceID() !== "webservice") - throw new FatalException("Invalid device id and type for webservice execution"); - - if (Request::GetGETUser() != Request::GetAuthUser()) - ZLog::Write(LOGLEVEL_INFO, sprintf("Webservice::HandleWebservice('%s'): user '%s' executing action for user '%s'", $commandCode, Request::GetAuthUser(), Request::GetGETUser())); - - // initialize non-wsdl soap server - $this->server = new SoapServer(null, array('uri' => "http://z-push.sf.net/webservice")); - - // the webservice command is handled by its class - if ($commandCode == ZPush::COMMAND_WEBSERVICE_DEVICE) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceDevice service", $commandCode)); - $this->server->setClass("WebserviceDevice"); - } - - // the webservice command is handled by its class - if ($commandCode == ZPush::COMMAND_WEBSERVICE_USERS) { - if (!defined("ALLOW_WEBSERVICE_USERS_ACCESS") || ALLOW_WEBSERVICE_USERS_ACCESS !== true) - throw new HTTPReturnCodeException("Access to the WebserviceUsers service is disabled in configuration. Enable setting ALLOW_WEBSERVICE_USERS_ACCESS", 403); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): executing WebserviceUsers service", $commandCode)); - - if(ZPush::GetBackend()->Setup("SYSTEM", true) == false) - throw new AuthenticationRequiredException(sprintf("User '%s' has no admin privileges", Request::GetAuthUser())); - - $this->server->setClass("WebserviceUsers"); - } - - $this->server->handle(); - - ZLog::Write(LOGLEVEL_DEBUG, sprintf("Webservice::HandleWebservice('%s'): sucessfully sent %d bytes", $commandCode, ob_get_length())); - return true; - } -} diff --git a/sources/lib/webservice/webservicedevice.php b/sources/lib/webservice/webservicedevice.php deleted file mode 100644 index 050ec2c..0000000 --- a/sources/lib/webservice/webservicedevice.php +++ /dev/null @@ -1,133 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class WebserviceDevice { - - /** - * Returns a list of all known devices of the Request::GetGETUser() - * - * @access public - * @return array - */ - public function ListDevicesDetails() { - $user = Request::GetGETUser(); - $devices = ZPushAdmin::ListDevices($user); - $output = array(); - - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::ListDevicesDetails(): found %d devices of user '%s'", count($devices), $user)); - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d devices", count($devices)), true); - - foreach ($devices as $devid) - $output[] = ZPushAdmin::GetDeviceDetails($devid, $user); - - return $output; - } - - /** - * Remove all state data for a device of the Request::GetGETUser() - * - * @param string $deviceId the device id - * - * @access public - * @return boolean - * @throws SoapFault - */ - public function RemoveDevice($deviceId) { - $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::RemoveDevice('%s'): remove device state data of user '%s'", $deviceId, Request::GetGETUser())); - - if (! ZPushAdmin::RemoveDevice(Request::GetGETUser(), $deviceId)) { - ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); - throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); - } - - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Removed device id '%s'", $deviceId), true); - return true; - } - - /** - * Marks a device of the Request::GetGETUser() to be remotely wiped - * - * @param string $deviceId the device id - * - * @access public - * @return boolean - * @throws SoapFault - */ - public function WipeDevice($deviceId) { - $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::WipeDevice('%s'): mark device of user '%s' for remote wipe", $deviceId, Request::GetGETUser())); - - if (! ZPushAdmin::WipeDevice(Request::GetAuthUser(), Request::GetGETUser(), $deviceId)) { - ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); - throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); - } - - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Wipe requested - device id '%s'", $deviceId), true); - return true; - } - - /** - * Marks a a device of the Request::GetGETUser() for resynchronization - * - * @param string $deviceId the device id - * - * @access public - * @return boolean - * @throws SoapFault - */ - public function ResyncDevice($deviceId) { - $deviceId = preg_replace("/[^A-Za-z0-9]/", "", $deviceId); - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceDevice::ResyncDevice('%s'): mark device of user '%s' for resynchronization", $deviceId, Request::GetGETUser())); - - if (! ZPushAdmin::ResyncDevice(Request::GetGETUser(), $deviceId)) { - ZPush::GetTopCollector()->AnnounceInformation(ZLog::GetLastMessage(LOGLEVEL_ERROR), true); - throw new SoapFault("ERROR", ZLog::GetLastMessage(LOGLEVEL_ERROR)); - } - - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Resync requested - device id '%s'", $deviceId), true); - return true; - } -} diff --git a/sources/lib/webservice/webserviceusers.php b/sources/lib/webservice/webserviceusers.php deleted file mode 100644 index 24d901b..0000000 --- a/sources/lib/webservice/webserviceusers.php +++ /dev/null @@ -1,100 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - -class WebserviceUsers { - - /** - * Returns a list of all known devices - * - * @access public - * @return array - */ - public function ListDevices() { - return ZPushAdmin::ListDevices(false); - } - - /** - * Returns a list of all known devices of the users - * - * @access public - * @return array - */ - public function ListDevicesAndUsers() { - $devices = ZPushAdmin::ListDevices(false); - $output = array(); - - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceUsers::ListDevicesAndUsers(): found %d devices", count($devices))); - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d devices and getting users", count($devices)), true); - - foreach ($devices as $devid) - $output[$devid] = ZPushAdmin::ListUsers($devid); - - return $output; - } - - /** - * Returns a list of all known devices with users and when they synchronized for the first time - * - * @access public - * @return array - */ - public function ListDevicesDetails() { - $devices = ZPushAdmin::ListDevices(false); - $output = array(); - - ZLog::Write(LOGLEVEL_INFO, sprintf("WebserviceUsers::ListLastSync(): found %d devices", count($devices))); - ZPush::GetTopCollector()->AnnounceInformation(sprintf("Retrieved details of %d devices and getting users", count($devices)), true); - - foreach ($devices as $deviceId) { - $output[$deviceId] = array(); - $users = ZPushAdmin::ListUsers($deviceId); - foreach ($users as $user) { - $output[$deviceId][$user] = ZPushAdmin::GetDeviceDetails($deviceId, $user); - } - } - - - return $output; - } -} diff --git a/sources/source_md5 b/sources/source_md5 new file mode 100644 index 0000000..b3fe9c5 --- /dev/null +++ b/sources/source_md5 @@ -0,0 +1 @@ +66eafc26c5e0a23a28ee43871d2be7e1 Z-Push-contrib-SHA1TOCHANGE.zip diff --git a/sources/source_sha1 b/sources/source_sha1 new file mode 100644 index 0000000..16c9841 --- /dev/null +++ b/sources/source_sha1 @@ -0,0 +1 @@ +682f127b7e5dc77da95eef6956579215d376b0e5 diff --git a/sources/source_url b/sources/source_url new file mode 100644 index 0000000..753f1bd --- /dev/null +++ b/sources/source_url @@ -0,0 +1 @@ +https://github.com/fmbiete/Z-Push-contrib/archive/SHA1TOCHANGE.zip diff --git a/sources/sql/mysql.sql b/sources/sql/mysql.sql deleted file mode 100644 index 36c9766..0000000 --- a/sources/sql/mysql.sql +++ /dev/null @@ -1,28 +0,0 @@ -create table zpush_settings (key_name varchar(50) not null, key_value varchar(50) not null, created_at datetime not null, updated_at datetime not null, primary key (key_name)); - -create table zpush_users (username varchar(50) not null, device_id varchar(50) not null, created_at datetime not null, updated_at datetime not null, primary key (username, device_id)); - -create table zpush_states (id_state integer auto_increment, device_id varchar(50) not null, uuid varchar(50), state_type varchar(50), counter integer, state_data mediumblob, - created_at datetime not null, updated_at datetime not null, primary key (id_state)); - -create unique index idx_zpush_states_unique on zpush_states (device_id, uuid, state_type, counter); - --- This is optional, and will require extra configuration in your mysql --- http://www.mysqlperformanceblog.com/2012/05/30/data-compression-in-innodb-for-text-and-blob-fields/ -alter table zpush_states engine=InnoDB row_format=compressed key_block_size=16; - - --- This table has a primary key id integer, because I will be linking a Rails model against it (admin wui) -create table zpush_preauth_users (id integer auto_increment, username varchar(50) not null, device_id varchar(50) not null, authorized boolean not null, - created_at datetime not null, updated_at datetime not null, primary key (id)); - -create unique index index_zpush_preauth_users_on_username_and_device_id on zpush_preauth_users (username, device_id); - -create table zpush_combined_usermap ( - username varchar(50) not null, - backend varchar(32) not null, - mappedname varchar(200) not null, - created_at datetime not null, - updated_at datetime not null, - primary key (username, backend) -); diff --git a/sources/testing/samples/meeting_reply_rim.txt b/sources/testing/samples/meeting_reply_rim.txt deleted file mode 100644 index b0eb1eb..0000000 --- a/sources/testing/samples/meeting_reply_rim.txt +++ /dev/null @@ -1,43 +0,0 @@ -BEGIN:VCALENDAR -PRODID:-//Research In Motion//RIM App//EN -VERSION:2.0 -CALSCALE:GREGORIAN -METHOD:REPLY -BEGIN:VTIMEZONE -TZID:Europe/London -BEGIN:STANDARD -DTSTART:20001029T020000 -RRULE:FREQ=YEARLY;BYDAY=4SU;BYMONTH=10 -TZNAME:GMT -TZOFFSETFROM:+0100 -TZOFFSETTO:+0000 -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:20000326T010000 -RRULE:FREQ=YEARLY;BYDAY=4SU;BYMONTH=3 -TZNAME:BST -TZOFFSETFROM:+0000 -TZOFFSETTO:+0100 -END:DAYLIGHT -END:VTIMEZONE -BEGIN:VEVENT -ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CN=User1;PARTSTAT=ACCEPTED:mailto:user1 - @domain1.tld -COMMENT:ok for me -CREATED:20150823T184526Z -DTEND;TZID=Europe/London:20150823T220000 -DTSTAMP:20150823T184527Z -DTSTART;TZID=Europe/London:20150823T210000 -ORGANIZER:mailto:user3@domain3.tld -SEQUENCE:0 -STATUS:CONFIRMED -SUMMARY:Test of meeting with Z-push -TRANSP:OPAQUE -UID:f503ce50-49c6-11e5-9533-693764eab43f -BEGIN:VALARM -ACTION:DISPLAY -DESCRIPTION:Test of meeting with Z-push in 15 minutes. -TRIGGER;RELATED=START:-PT15M -END:VALARM -END:VEVENT -END:VCALENDAR diff --git a/sources/testing/samples/meeting_request.txt b/sources/testing/samples/meeting_request.txt deleted file mode 100644 index 302c189..0000000 --- a/sources/testing/samples/meeting_request.txt +++ /dev/null @@ -1,53 +0,0 @@ -BEGIN:VCALENDAR -METHOD:REQUEST -PRODID:Microsoft Exchange Server 2010 -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:Romance Standard Time -BEGIN:STANDARD -DTSTART:16010101T030000 -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:16010101T020000 -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 -END:DAYLIGHT -END:VTIMEZONE -BEGIN:VEVENT -ORGANIZER;CN=Pablo Marmol:MAILTO:pablo.marmol@zpush.org -ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE;CN=Name1 Sur - name2:MAILTO:user1@zpush.org -ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Name1 - Surname2:MAILTO:user2@zpush.org -DESCRIPTION;LANGUAGE=es-ES:Texto de la segunda cita\n\n -SUMMARY;LANGUAGE=es-ES:Segunda cita -DTSTART;TZID=Romance Standard Time:20140519T090000 -DTEND;TZID=Romance Standard Time:20140519T093000 -UID:040000008200E00074C5B7101A82E0080000000070BC3EB80871CF01000000000000000 - 010000000B3492E4691795F4E810CCD60A178B53C -CLASS:PUBLIC -PRIORITY:5 -DTSTAMP:20140516T111408Z -TRANSP:OPAQUE -STATUS:CONFIRMED -SEQUENCE:0 -LOCATION;LANGUAGE=es-ES:Oficina -X-MICROSOFT-CDO-APPT-SEQUENCE:0 -X-MICROSOFT-CDO-OWNERAPPTID:860903390 -X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE -X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY -X-MICROSOFT-CDO-ALLDAYEVENT:FALSE -X-MICROSOFT-CDO-IMPORTANCE:1 -X-MICROSOFT-CDO-INSTTYPE:0 -X-MICROSOFT-DISALLOW-COUNTER:FALSE -BEGIN:VALARM -ACTION:DISPLAY -DESCRIPTION:REMINDER -TRIGGER;RELATED=START:-PT15M -END:VALARM -END:VEVENT -END:VCALENDAR diff --git a/sources/testing/samples/meeting_request_rim.txt b/sources/testing/samples/meeting_request_rim.txt deleted file mode 100644 index 245f4ad..0000000 --- a/sources/testing/samples/meeting_request_rim.txt +++ /dev/null @@ -1,38 +0,0 @@ -BEGIN:VCALENDAR -PRODID:-//Research In Motion//RIM App//EN -VERSION:2.0 -CALSCALE:GREGORIAN -METHOD:REQUEST -BEGIN:VTIMEZONE -TZID:Europe/Dublin -BEGIN:STANDARD -DTSTART:20001029T020000 -RRULE:FREQ=3DYEARLY;BYDAY=3D4SU;BYMONTH=3D10 -TZNAME:GMT -TZOFFSETFROM:+0100 -TZOFFSETTO:+0000 -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:20000326T010000 -RRULE:FREQ=3DYEARLY;BYDAY=3D4SU;BYMONTH=3D3 -TZNAME:IST -TZOFFSETFROM:+0000 -TZOFFSETTO:+0100 -END:DAYLIGHT -END:VTIMEZONE -BEGIN:VEVENT -ATTENDEE;RSVP=3DTRUE;ROLE=3DREQ-PARTICIPANT;CN=3Dmaxtest@mcgva.ovh;PARTSTAT= -=3DTENTATIVE -:mailto:user1@domain.tld -CREATED:20150814T163344Z -DTEND;TZID=3DEurope/Dublin:20150814T175000 -DTSTAMP:20150814T163344Z -DTSTART;TZID=3DEurope/Dublin:20150814T174500 -ORGANIZER:mailto:user2@domain.tld -SEQUENCE:0 -STATUS:CONFIRMED -SUMMARY:Test invitation -TRANSP:OPAQUE -UID:3af267ec-42a2-11e5-bfc0-574eadb945a2 -END:VEVENT -END:VCALENDAR \ No newline at end of file diff --git a/sources/testing/samples/messages/emoticon.txt b/sources/testing/samples/messages/emoticon.txt deleted file mode 100644 index 93f64b8..0000000 --- a/sources/testing/samples/messages/emoticon.txt +++ /dev/null @@ -1,28 +0,0 @@ -Return-Path: -X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on xxxxx -X-Spam-Level: -X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED autolearn=ham - autolearn_force=no version=3.4.0 -Delivered-To: xxxx@xxxxxxxxxxxx -Received: from localhost (xxxx.xxxxxxxxx.xx [127.0.0.1]) - by xxxx.xxxxxxxxxxx.xx (Postfix) with ESMTP id D8AB65001D6 - for ; Sun, 20 Jul 2014 15:30:58 +0200 (CEST) -X-Virus-Scanned: Debian amavisd-new at xxxx.xxxxxxxxx.xx -Received: from xxxx.xxxxxxxxx.xxx ([127.0.0.1]) - by localhost (xxxx.xxxxxxxxxx.xx [127.0.0.1]) (amavisd-new, port 10024) - with ESMTP id vI3uJWoUh-Pz for ; - Sun, 20 Jul 2014 15:30:58 +0200 (CEST) -Received-SPF: none (xxxx.xxx: No applicable sender policy available) receiver=xxxx.xxxxxxxxxx.xx; identity=mailfrom; envelope-from="xxxx.xxxxx@xxxx.xxxx"; helo=$ -From: xxxxxxxxxx -Content-Type: text/plain; - charset=utf-8 -Content-Transfer-Encoding: quoted-printable -Mime-Version: 1.0 (1.0) -Subject: Testing -Message-Id: -Date: Sun, 20 Jul 2014 15:31:09 +0200 -To: xxxx xxxxx - -Testing emojis =F0=9F=98=84 - -Sendt fra min iPad= diff --git a/sources/testing/samples/messages/emoticon_base64.txt b/sources/testing/samples/messages/emoticon_base64.txt deleted file mode 100644 index ca768cb..0000000 --- a/sources/testing/samples/messages/emoticon_base64.txt +++ /dev/null @@ -1,39 +0,0 @@ -Return-Path: xxxxxx@xxxxxxxx.xxx -X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.xxxxxxxxx.xx -X-Spam-Level: -X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED, HEADER_FROM_DIFFERENT_DOMAINS autolearn=unavailable autolearn_force=no version=3.4.0 -Delivered-To: xxxxxxx@xxxxxxxxxxx.xx -Received: from localhost (xxxxx.xxxxxx.xx [127.0.0.1]) by xxxxx.xxxxxxxxxx.xx (Postfix) with ESMTP id 1982C500AD4; Fri, 11 Jul 2014 21:20:53 +0200 (CEST) -X-Virus-Scanned: Debian amavisd-new at xxxxx.xxxxxxx.xx -Received: from xxxxx.xxxxxxxxxx.xx ([127.0.0.1]) by localhost (xxxx.xxxxxxxx.xx [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2nP27OZ1CUzN; Fri, 11 Jul 2014 21:20:52 +0200 (CEST) -Resent-From: xxxxx@xxxxxxx.xx -DMARC-Filter: OpenDMARC Filter v1.2.0 xxxx.xxxxxx.xxx 9Dxxx7500A9D -X-Virus-Scanned: Debian amavisd-new at xxxxx.xxxxxxxxxx.xx -MIME-Version: 1.0 -DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=xxxxxxxx.xx; s=mail; t=1405106446; bh=ufc8aM3vMmQISb8aKXpA3Rk8t/+ce6kFhZUr1x6DwtU=; h=From:Subject:Date:To:From; b=uQb7eWfRZdkUF+didgfmgJTWW78GdyHJlEqhz+WFl2Ouqkt7iXZPwljR4OIoFvumK1IJqsXWRW74Py+Mq2CWkxeuML/9kTrWBXJDMichOqll666EgG8/DyyYH6RPIhje9ygILX85E4VO0zO1N4uh0Tc/D0Ow93hUWS51D1cX7gA= -From: xxxxx@xxxxxxxx.xx -Subject: Kontroll -Message-ID: 28EF0B82-61D9-4BA3-B534-CE4FB6101D1A@xxxxxx.xxx -Date: Fri, 11 Jul 2014 21:21:03 +0200 -To: stxxx xxxxx@xxxxxxxxx.xx -Content-Type: multipart/mixed; boundary="=_83c3cde24966264fd2b3537156aaaba6" -Received-SPF: Pass (xxxx.xxxxxxxx.loc: domain of xxxxxx@xxxxxx.xx designates xxx.xxx.x.xx as permitted sender) receiver=xxxx.xxxxxxxx.xxx; client-ip=xxx.xxx.x.xx; helo=xxxxx.xxxxxxx.xx; -X-Auto-Response-Suppress: DR, OOF, AutoReply -Resent-Message-Id: 20140711192053.1982C500AD4@xxxxxx.xxxxxx.xx -Resent-Date: Fri, 11 Jul 2014 21:20:53 +0200 (CEST) - ---=_83c3cde24966264fd2b3537156aaaba6 -Content-Type: multipart/alternative; boundary="=_3eaa2d9769aa280737bc3e5268efb2ad" - ---=_3eaa2d9769aa280737bc3e5268efb2ad -Content-Transfer-Encoding: base64 -Content-Type: text/plain; charset="utf-8" - -RGV0IGVyIHDDpSB0aWRlIGF0dCB2aSBwbGFubGVnZ2VyIHVuaWZvcm1zIGtvbnRyb2xsIA0KS3Zh -ciBoZWxnIG5vZW4gc29tIGlra2plIGhhcg0KQWxsZSBtw6Uga3VubmUgZ2rDuHIgZW4gaW5uc2F0 -cyBldHRlciBmZXJpZW4gMiAgcGVycyB4IDQgaGVsZ2VyIHPDpSB0cm9yIGVnIG15ZSBhdiBwcm9i -bGVtbWV0IGVyIHZla2tlDQpHb2Qgc29tbWVyIG9nIGZlcmllIPCfmI4NClZpYmVrZQ0KDQpTZW5k -dCBmcmEgbWluIGlQaG9uZQ== ---=_3eaa2d9769aa280737bc3e5268efb2ad-- - ---=_83c3cde24966264fd2b3537156aaaba6-- diff --git a/sources/testing/samples/messages/emoticon_subject.txt b/sources/testing/samples/messages/emoticon_subject.txt deleted file mode 100644 index cc17c76..0000000 --- a/sources/testing/samples/messages/emoticon_subject.txt +++ /dev/null @@ -1,20 +0,0 @@ -Mime-Version: 1.0 -From: xxxx@xxxxxxx.xxx -Subject: =?utf-8?B?8J+YhA==?= -Message-id: EC08EA2B-1439-423D-AA69-843776D5C39C@xxxxxxxx.xx -Date: Wed, 23 Jul 2014 19:50:36 +0200 -To: test2014@xxxxxxxxxxx.xx -Content-Type: multipart/mixed; boundary="=_72ced2dca575896ff9586958e1bb6592" - -This is a multi-part message in MIME format. ---=_72ced2dca575896ff9586958e1bb6592 -Content-Type: multipart/alternative; boundary="=_9818f474f7be6252ec285590ed281857" - ---=_9818f474f7be6252ec285590ed281857 -Content-Transfer-Encoding: base64 -Content-Type: text/plain; charset=utf-8 - -8J+Y4oCeDQoNClNlbmR0IGZyYSBtaW4gaVBob25l ---=_9818f474f7be6252ec285590ed281857-- - ---=_72ced2dca575896ff9586958e1bb6592-- diff --git a/sources/testing/samples/messages/french.txt b/sources/testing/samples/messages/french.txt deleted file mode 100644 index 15c3490..0000000 --- a/sources/testing/samples/messages/french.txt +++ /dev/null @@ -1,48 +0,0 @@ -Return-Path: -Delivered-To: informatique@domain.com -Received: from localhost (localhost.localdomain [127.0.0.1]) - by domain.com (Postfix) with ESMTP id AB32E1000EA - for ; Sun, 7 Sep 2014 09:50:21 +0200 (CEST) -Authentication-Results: domain.com (amavisd-new); - dkim=pass (1024-bit key) reason="pass (just generated, assumed good)" - header.d=domain.com -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d= - domain.com; h=content-transfer-encoding:content-type - :content-type:mime-version:from:from:subject:subject:date:date - :received; s=mail; t=1410076220; x=1411890621; bh=paEf4SYhXmrsFW - vtqTtrw7K9W9eprm8TMRs5JiO2p6o=; b=Dszu0yEi/ktiqQoFdLPKxkvdDB+sU+ - 3b+B2O0Jf2PzVahHvMj7bGJSoW0eVb1u6tot6q5NOjFH85Ab1MZdhoga+jMW3h8E - 5GrVvA4NumMV/HMJ8M/1LpAc5nTaGxN+8KfGHxCM0DsjZEFwuLwL3xU6PgSrggcC - 6DT6GcpnPTRxM= -X-Virus-Scanned: Debian amavisd-new at domain.com -X-Spam-Flag: NO -X-Spam-Score: 0.138 -X-Spam-Level: -X-Spam-Status: No, score=0.138 tagged_above=-999 required=4 - tests=[MISSING_MID=0.14, NO_RECEIVED=-0.001, NO_RELAYS=-0.001] - autolearn=no -Received: from domain.com ([127.0.0.1]) - by localhost (domain.com [127.0.0.1]) (amavisd-new, port 10024) - with ESMTP id YDofG_pOTs47 for ; - Sun, 7 Sep 2014 09:50:20 +0200 (CEST) -Date: Sun, 07 Sep 2014 09:50:03 +0200 -Subject: =?ISO-8859-1?Q?Re:_[TC5_BAT]_Construction_g=E9n=E9rale_/_08:30?= -From: =?ISO-8859-1?Q?Edt_Secr=E9tariat_ESITC_Cachan?= - -To: Informatique ESITC -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf-8 -Content-Transfer-Encoding: base64 -Message-Id: <20140907075021.AB32E1000EA@domain.com> - -Qydlc3QgcmVjdGlmacOpLgpCb25uZSBqb3VybsOpZS4KTGUgc2VjcsOpdGFyaWF0IGVkdMKgIGRl -IGwnRVNJVEMKCkxlIDYgc2VwdC4gMjAxNCAyMzo0NCwgSW5mb3JtYXRpcXVlIEVTSVRDIDxpbmZv -cm1hdGlxdWVAYWRtLmVzaXRjLWNhY2hhbi5mcj4gYSDDqWNyaXQgOgo+Cj4gQm9uam91ciwgCj4K -PiBJbCBzZW1ibGVyYWl0IHF1J2lsIHkgYWl0IHVuZSBwZXRpdGUgZXJyZXVyIHN1ciBsJ2VtcGxv -aSBkdSB0ZW1wcyBkZXMgCj4gVEM1IEJBVC4gCj4KPiBMZSBMdW5kaSAwNSBKYW52aWVyIDIwMTUg -w6AgMDhIMzAuIExlIGNvdXJzIGRlIENHIGR1cmUgMWgzMCBhbG9ycyBxdSdpbCB5IAo+IGEgMiBz -w6lhbmNlcy4gCj4gRG9uYyBzb2l0IGMnZXN0IAo+IFNvaXQgdW5lIHPDqWFuY2UgZGUgMUgzMCAK -PiBTb2l0IDIgc8OpYW5jZXMgZGUgM0ggKGF1IHRvdGFsKSAKPiBNYWlzIHBhcyAyIHPDqWFuY2Vz -IGRlIDFIMzAgKGF1IHRvdGFsKSAKPgo+IENvcmRpYWxlbWVudCwgCj4gSW5mb3JtYXRpcXVlIEVT -SVRDIAo= - diff --git a/sources/testing/samples/messages/m0001.txt b/sources/testing/samples/messages/m0001.txt deleted file mode 100644 index 5615811..0000000 --- a/sources/testing/samples/messages/m0001.txt +++ /dev/null @@ -1,31 +0,0 @@ -From: "Doug Sauder" -To: "Jürgen Schmürgen" -Subject: Die Hasen und die Frösche (Microsoft Outlook 00) -Date: Wed, 17 May 2000 19:08:29 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: 8bit -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Frösche - -Die Hasen klagten einst über ihre mißliche Lage; "wir leben", sprach ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der -Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger als der Tod -selbst. Auf, laßt uns ein für allemal sterben." - -In einem nahen Teich wollten sie sich nun ersäufen; sie eilten ihm zu; -allein das außerordentliche Getöse und ihre wunderbare Gestalt erschreckte -eine Menge Frösche, die am Ufer saßen, so sehr, daß sie aufs schnellste -untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen noch ein -wenig aufschieben, denn auch uns fürchten, wie ihr seht, einige Tiere, -welche also wohl noch unglücklicher sein müssen als wir." - diff --git a/sources/testing/samples/messages/m0002.txt b/sources/testing/samples/messages/m0002.txt deleted file mode 100644 index b98b41d..0000000 --- a/sources/testing/samples/messages/m0002.txt +++ /dev/null @@ -1,31 +0,0 @@ -From: "Doug Sauder" -To: "Jürgen Schmürgen" -Subject: Die Hasen und die Frösche (Microsoft Outlook 00) -Date: Wed, 17 May 2000 19:10:31 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der = -Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist = -=E4rger als der Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; = -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt = -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF = -sie aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch = -ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige = -Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - diff --git a/sources/testing/samples/messages/m0003.txt b/sources/testing/samples/messages/m0003.txt deleted file mode 100644 index c5109ef..0000000 --- a/sources/testing/samples/messages/m0003.txt +++ /dev/null @@ -1,30 +0,0 @@ -From: "Doug Sauder" -To: "Jürgen Schmürgen" -Subject: Die Hasen und die Frösche (Microsoft Outlook 00) -Date: Wed, 17 May 2000 19:11:50 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: base64 -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -RGllIEhhc2VuIHVuZCBkaWUgRnL2c2NoZQ0KDQpEaWUgSGFzZW4ga2xhZ3RlbiBlaW5zdCD8YmVy -IGlocmUgbWnfbGljaGUgTGFnZTsgIndpciBsZWJlbiIsIHNwcmFjaCBlaW4gUmVkbmVyLCAiaW4g -c3RldGVyIEZ1cmNodCB2b3IgTWVuc2NoZW4gdW5kIFRpZXJlbiwgZWluZSBCZXV0ZSBkZXIgSHVu -ZGUsIGRlciBBZGxlciwgamEgZmFzdCBhbGxlciBSYXVidGllcmUhIFVuc2VyZSBzdGV0ZSBBbmdz -dCBpc3Qg5HJnZXIgYWxzIGRlciBUb2Qgc2VsYnN0LiBBdWYsIGxh33QgdW5zIGVpbiBm/HIgYWxs -ZW1hbCBzdGVyYmVuLiIgDQoNCkluIGVpbmVtIG5haGVuIFRlaWNoIHdvbGx0ZW4gc2llIHNpY2gg -bnVuIGVyc+R1ZmVuOyBzaWUgZWlsdGVuIGlobSB6dTsgYWxsZWluIGRhcyBhdd9lcm9yZGVudGxp -Y2hlIEdldPZzZSB1bmQgaWhyZSB3dW5kZXJiYXJlIEdlc3RhbHQgZXJzY2hyZWNrdGUgZWluZSBN -ZW5nZSBGcvZzY2hlLCBkaWUgYW0gVWZlciBzYd9lbiwgc28gc2VociwgZGHfIHNpZSBhdWZzIHNj -aG5lbGxzdGUgdW50ZXJ0YXVjaHRlbi4gDQoNCiJIYWx0IiwgcmllZiBudW4gZWJlbiBkaWVzZXIg -U3ByZWNoZXIsICJ3aXIgd29sbGVuIGRhcyBFcnPkdWZlbiBub2NoIGVpbiB3ZW5pZyBhdWZzY2hp -ZWJlbiwgZGVubiBhdWNoIHVucyBm/HJjaHRlbiwgd2llIGlociBzZWh0LCBlaW5pZ2UgVGllcmUs -IHdlbGNoZSBhbHNvIHdvaGwgbm9jaCB1bmds/GNrbGljaGVyIHNlaW4gbfxzc2VuIGFscyB3aXIu -IiANCg== - diff --git a/sources/testing/samples/messages/m0004.txt b/sources/testing/samples/messages/m0004.txt deleted file mode 100644 index fe16b2a..0000000 --- a/sources/testing/samples/messages/m0004.txt +++ /dev/null @@ -1,31 +0,0 @@ -From: "Doug Sauder" -To: =?iso-8859-1?B?SvxyZ2VuIFNjaG38cmdlbg==?= -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:13:51 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: 8bit -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Frösche - -Die Hasen klagten einst über ihre mißliche Lage; "wir leben", sprach ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der -Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger als der Tod -selbst. Auf, laßt uns ein für allemal sterben." - -In einem nahen Teich wollten sie sich nun ersäufen; sie eilten ihm zu; -allein das außerordentliche Getöse und ihre wunderbare Gestalt erschreckte -eine Menge Frösche, die am Ufer saßen, so sehr, daß sie aufs schnellste -untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen noch ein -wenig aufschieben, denn auch uns fürchten, wie ihr seht, einige Tiere, -welche also wohl noch unglücklicher sein müssen als wir." - diff --git a/sources/testing/samples/messages/m0005.txt b/sources/testing/samples/messages/m0005.txt deleted file mode 100644 index 56f1089..0000000 --- a/sources/testing/samples/messages/m0005.txt +++ /dev/null @@ -1,31 +0,0 @@ -From: "Doug Sauder" -To: =?iso-8859-1?B?SvxyZ2VuIFNjaG38cmdlbg==?= -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:15:35 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der = -Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist = -=E4rger als der Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; = -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt = -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF = -sie aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch = -ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige = -Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - diff --git a/sources/testing/samples/messages/m0006.txt b/sources/testing/samples/messages/m0006.txt deleted file mode 100644 index a51ea70..0000000 --- a/sources/testing/samples/messages/m0006.txt +++ /dev/null @@ -1,34 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" , - =?utf-7?B?SitBUHctcmdlbiBTY2htK0FQdy1yZ2Vu?= -Subject: =?utf-7?Q?Die_Hasen_und_die_Fr+APY-sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:18:39 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="utf-7" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr+APY-sche - -Die Hasen klagten einst +APw-ber ihre mi+AN8-liche Lage+ADs- +ACI-wir = -leben+ACI-, sprach ein Redner, +ACI-in steter Furcht vor Menschen und = -Tieren, eine Beute der Hunde, der Adler, ja fast aller Raubtiere+ACE- = -Unsere stete Angst ist +AOQ-rger als der Tod selbst. Auf, la+AN8-t uns = -ein f+APw-r allemal sterben.+ACI-=20 - -In einem nahen Teich wollten sie sich nun ers+AOQ-ufen+ADs- sie eilten = -ihm zu+ADs- allein das au+AN8-erordentliche Get+APY-se und ihre = -wunderbare Gestalt erschreckte eine Menge Fr+APY-sche, die am Ufer = -sa+AN8-en, so sehr, da+AN8- sie aufs schnellste untertauchten.=20 - -+ACI-Halt+ACI-, rief nun eben dieser Sprecher, +ACI-wir wollen das = -Ers+AOQ-ufen noch ein wenig aufschieben, denn auch uns f+APw-rchten, wie = -ihr seht, einige Tiere, welche also wohl noch ungl+APw-cklicher sein = -m+APw-ssen als wir.+ACI-=20 - diff --git a/sources/testing/samples/messages/m0007.txt b/sources/testing/samples/messages/m0007.txt deleted file mode 100644 index cd5c589..0000000 --- a/sources/testing/samples/messages/m0007.txt +++ /dev/null @@ -1,32 +0,0 @@ -From: "Doug Sauder" -To: =?utf-7?Q?Heinz_M+APw-ller?= -Subject: =?utf-7?Q?Die_Hasen_und_die_Fr+APY-sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:20:24 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="utf-7" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr+APY-sche - -Die Hasen klagten einst +APw-ber ihre mi+AN8-liche Lage+ADs- +ACI-wir = -leben+ACI-, sprach ein Redner, +ACI-in steter Furcht vor Menschen und = -Tieren, eine Beute der Hunde, der Adler, ja fast aller Raubtiere+ACE- = -Unsere stete Angst ist +AOQ-rger als der Tod selbst. Auf, la+AN8-t uns = -ein f+APw-r allemal sterben.+ACI-=20 - -In einem nahen Teich wollten sie sich nun ers+AOQ-ufen+ADs- sie eilten = -ihm zu+ADs- allein das au+AN8-erordentliche Get+APY-se und ihre = -wunderbare Gestalt erschreckte eine Menge Fr+APY-sche, die am Ufer = -sa+AN8-en, so sehr, da+AN8- sie aufs schnellste untertauchten.=20 - -+ACI-Halt+ACI-, rief nun eben dieser Sprecher, +ACI-wir wollen das = -Ers+AOQ-ufen noch ein wenig aufschieben, denn auch uns f+APw-rchten, wie = -ihr seht, einige Tiere, welche also wohl noch ungl+APw-cklicher sein = -m+APw-ssen als wir.+ACI-=20 diff --git a/sources/testing/samples/messages/m0008.txt b/sources/testing/samples/messages/m0008.txt deleted file mode 100644 index 80c620c..0000000 --- a/sources/testing/samples/messages/m0008.txt +++ /dev/null @@ -1,32 +0,0 @@ -From: "Doug Sauder" -To: =?utf-8?Q?Heinz_M=C3=BCller?= -Subject: =?utf-8?Q?Die_Hasen_und_die_Fr=C3=B6sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:27:04 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="utf-8" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr=C3=B6sche - -Die Hasen klagten einst =C3=BCber ihre mi=C3=9Fliche Lage; "wir leben", = -sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute = -der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist = -=C3=A4rger als der Tod selbst. Auf, la=C3=9Ft uns ein f=C3=BCr allemal = -sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=C3=A4ufen; sie eilten ihm = -zu; allein das au=C3=9Ferordentliche Get=C3=B6se und ihre wunderbare = -Gestalt erschreckte eine Menge Fr=C3=B6sche, die am Ufer sa=C3=9Fen, so = -sehr, da=C3=9F sie aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=C3=A4ufen = -noch ein wenig aufschieben, denn auch uns f=C3=BCrchten, wie ihr seht, = -einige Tiere, welche also wohl noch ungl=C3=BCcklicher sein m=C3=BCssen = -als wir."=20 diff --git a/sources/testing/samples/messages/m0009.txt b/sources/testing/samples/messages/m0009.txt deleted file mode 100644 index e25390c..0000000 --- a/sources/testing/samples/messages/m0009.txt +++ /dev/null @@ -1,27 +0,0 @@ -From: "Doug Sauder" -To: "Heinz Müller" -Subject: Die Hasen und die Frösche (Microsoft Outlook 00) -Date: Wed, 17 May 2000 19:28:40 -0400 -Message-ID: -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Frösche - -Die Hasen klagten einst über ihre mißliche Lage; "wir leben", sprach ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der -Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger als der Tod -selbst. Auf, laßt uns ein für allemal sterben." - -In einem nahen Teich wollten sie sich nun ersäufen; sie eilten ihm zu; -allein das außerordentliche Getöse und ihre wunderbare Gestalt erschreckte -eine Menge Frösche, die am Ufer saßen, so sehr, daß sie aufs schnellste -untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen noch ein -wenig aufschieben, denn auch uns fürchten, wie ihr seht, einige Tiere, -welche also wohl noch unglücklicher sein müssen als wir." - diff --git a/sources/testing/samples/messages/m0010.txt b/sources/testing/samples/messages/m0010.txt deleted file mode 100644 index e353439..0000000 --- a/sources/testing/samples/messages/m0010.txt +++ /dev/null @@ -1,30 +0,0 @@ -From: "Doug Sauder" -To: =?Windows-1252?Q?Heinz_M=FCller?= -Subject: =?Windows-1252?Q?Die_Hasen_und_die_Fr=F6sche_=28Microsoft_Outlook_00=29?= -Date: Wed, 17 May 2000 19:30:20 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: text/plain; - charset="Windows-1252" -Content-Transfer-Encoding: quoted-printable -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der = -Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist = -=E4rger als der Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; = -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt = -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF = -sie aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch = -ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige = -Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 diff --git a/sources/testing/samples/messages/m0011.txt b/sources/testing/samples/messages/m0011.txt deleted file mode 100644 index 95eb05a..0000000 --- a/sources/testing/samples/messages/m0011.txt +++ /dev/null @@ -1,135 +0,0 @@ -From: "Doug Sauder" -To: =?iso-8859-1?Q?Heinz_M=FCller?= -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:32:47 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/mixed; - boundary="----=_NextPart_000_0002_01BFC036.AE309650" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_0002_01BFC036.AE309650 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -The Hare and the Tortoise=20 -=20 -A HARE one day ridiculed the short feet and slow pace of the Tortoise, = -who replied, laughing: "Though you be swift as the wind, I will beat = -you in a race." The Hare, believing her assertion to be simply = -impossible, assented to the proposal; and they agreed that the Fox = -should choose the course and fix the goal. On the day appointed for the = -race the two started together. The Tortoise never for a moment stopped, = -but went on with a slow but steady pace straight to the end of the = -course. The Hare, lying down by the wayside, fell fast asleep. At last = -waking up, and moving as fast as he could, he saw the Tortoise had = -reached the goal, and was comfortably dozing after her fatigue. =20 -=20 -Slow but steady wins the race. =20 - -------=_NextPart_000_0002_01BFC036.AE309650 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= - -------=_NextPart_000_0002_01BFC036.AE309650 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= - -------=_NextPart_000_0002_01BFC036.AE309650 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== - -------=_NextPart_000_0002_01BFC036.AE309650-- diff --git a/sources/testing/samples/messages/m0012.txt b/sources/testing/samples/messages/m0012.txt deleted file mode 100644 index 44bbe3b..0000000 --- a/sources/testing/samples/messages/m0012.txt +++ /dev/null @@ -1,43 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:35:05 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="redball.png" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== diff --git a/sources/testing/samples/messages/m0013.txt b/sources/testing/samples/messages/m0013.txt deleted file mode 100644 index d9a38fa..0000000 --- a/sources/testing/samples/messages/m0013.txt +++ /dev/null @@ -1,83 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:36:13 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/mixed; - boundary="----=_NextPart_000_0004_01BFC037.28F2FA90" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_0004_01BFC037.28F2FA90 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= - -------=_NextPart_000_0004_01BFC037.28F2FA90 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== - -------=_NextPart_000_0004_01BFC037.28F2FA90-- diff --git a/sources/testing/samples/messages/m0014.txt b/sources/testing/samples/messages/m0014.txt deleted file mode 100644 index 213493c..0000000 --- a/sources/testing/samples/messages/m0014.txt +++ /dev/null @@ -1,72 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:38:28 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/alternative; - boundary="----=_NextPart_000_0005_01BFC037.799BEF60" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_0005_01BFC037.799BEF60 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -The Hare and the Tortoise=20 -=20 -A HARE one day ridiculed the short feet and slow pace of the Tortoise, = -who replied, laughing: "Though you be swift as the wind, I will beat = -you in a race." The Hare, believing her assertion to be simply = -impossible, assented to the proposal; and they agreed that the Fox = -should choose the course and fix the goal. On the day appointed for the = -race the two started together. The Tortoise never for a moment stopped, = -but went on with a slow but steady pace straight to the end of the = -course. The Hare, lying down by the wayside, fell fast asleep. At last = -waking up, and moving as fast as he could, he saw the Tortoise had = -reached the goal, and was comfortably dozing after her fatigue. =20 -=20 -Slow but steady wins the race. =20 - - -------=_NextPart_000_0005_01BFC037.799BEF60 -Content-Type: text/html; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - - - - -

The Hare and the Tortoise = -
 
A HARE=20 -one day ridiculed the short feet and slow pace of the Tortoise, who = -replied,=20 -laughing:  "Though you be swift as the wind, I will beat you in a=20 -race."  The Hare, believing her assertion to be simply impossible, = -assented=20 -to the proposal; and they agreed that the Fox should choose the course = -and fix=20 -the goal.  On the day appointed for the race the two started=20 -together.  The Tortoise never for a moment stopped, but went on = -with a slow=20 -but steady pace straight to the end of the course.  The Hare, lying = -down by=20 -the wayside, fell fast asleep.  At last waking up, and moving as = -fast as he=20 -could, he saw the Tortoise had reached the goal, and was comfortably = -dozing=20 -after her fatigue. 
 
Slow but steady wins the = -race. =20 -
- -------=_NextPart_000_0005_01BFC037.799BEF60-- diff --git a/sources/testing/samples/messages/m0015.txt b/sources/testing/samples/messages/m0015.txt deleted file mode 100644 index 353f096..0000000 --- a/sources/testing/samples/messages/m0015.txt +++ /dev/null @@ -1,144 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:42:10 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/mixed; - boundary="----=_NextPart_000_0008_01BFC037.FDD8EE90" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_0008_01BFC037.FDD8EE90 -Content-Type: multipart/alternative; - boundary="----=_NextPart_001_0009_01BFC037.FDD8EE90" - - -------=_NextPart_001_0009_01BFC037.FDD8EE90 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -The Hare and the Tortoise=20 -=20 -A HARE one day ridiculed the short feet and slow pace of the Tortoise, = -who replied, laughing: "Though you be swift as the wind, I will beat = -you in a race." The Hare, believing her assertion to be simply = -impossible, assented to the proposal; and they agreed that the Fox = -should choose the course and fix the goal. On the day appointed for the = -race the two started together. The Tortoise never for a moment stopped, = -but went on with a slow but steady pace straight to the end of the = -course. The Hare, lying down by the wayside, fell fast asleep. At last = -waking up, and moving as fast as he could, he saw the Tortoise had = -reached the goal, and was comfortably dozing after her fatigue. =20 -=20 -Slow but steady wins the race. =20 - - -------=_NextPart_001_0009_01BFC037.FDD8EE90 -Content-Type: text/html; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - - - - -

The Hare and the Tortoise = -
 
A HARE=20 -one day ridiculed the short feet and slow pace of the Tortoise, who = -replied,=20 -laughing:  "Though you be swift as the wind, I will beat you in a=20 -race."  The Hare, believing her assertion to be simply impossible, = -assented=20 -to the proposal; and they agreed that the Fox should choose the course = -and fix=20 -the goal.  On the day appointed for the race the two started=20 -together.  The Tortoise never for a moment stopped, but went on = -with a slow=20 -but steady pace straight to the end of the course.  The Hare, lying = -down by=20 -the wayside, fell fast asleep.  At last waking up, and moving as = -fast as he=20 -could, he saw the Tortoise had reached the goal, and was comfortably = -dozing=20 -after her fatigue. 
 
Slow but steady wins the = -race. =20 -
- -------=_NextPart_001_0009_01BFC037.FDD8EE90-- - -------=_NextPart_000_0008_01BFC037.FDD8EE90 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= - -------=_NextPart_000_0008_01BFC037.FDD8EE90 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== - -------=_NextPart_000_0008_01BFC037.FDD8EE90-- diff --git a/sources/testing/samples/messages/m0016.txt b/sources/testing/samples/messages/m0016.txt deleted file mode 100644 index c121613..0000000 --- a/sources/testing/samples/messages/m0016.txt +++ /dev/null @@ -1,156 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:44:45 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/related; - boundary="----=_NextPart_000_000C_01BFC038.5A5C8E60" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_000C_01BFC038.5A5C8E60 -Content-Type: multipart/alternative; - boundary="----=_NextPart_001_000D_01BFC038.5A5C8E60" - - -------=_NextPart_001_000D_01BFC038.5A5C8E60 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -The Hare and the Tortoise=20 -=20 -A HARE one day ridiculed the short feet and slow pace of the Tortoise, = -who replied, laughing: "Though you be swift as the wind, I will beat = -you in a race." The Hare, believing her assertion to be simply = -impossible, assented to the proposal; and they agreed that the Fox = -should choose the course and fix the goal. On the day appointed for the = -race the two started together. The Tortoise never for a moment stopped, = -but went on with a slow but steady pace straight to the end of the = -course. The Hare, lying down by the wayside, fell fast asleep. At last = -waking up, and moving as fast as he could, he saw the Tortoise had = -reached the goal, and was comfortably dozing after her fatigue. =20 - - -=20 -Slow but steady wins the race. =20 - - - - -------=_NextPart_001_000D_01BFC038.5A5C8E60 -Content-Type: text/html; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - - - - -

The Hare and the Tortoise = -
 
A HARE=20 -one day ridiculed the short feet and slow pace of the Tortoise, who = -replied,=20 -laughing:  "Though you be swift as the wind, I will beat you in a=20 -race."  The Hare, believing her assertion to be simply impossible, = -assented=20 -to the proposal; and they agreed that the Fox should choose the course = -and fix=20 -the goal.  On the day appointed for the race the two started=20 -together.  The Tortoise never for a moment stopped, but went on = -with a slow=20 -but steady pace straight to the end of the course.  The Hare, lying = -down by=20 -the wayside, fell fast asleep.  At last waking up, and moving as = -fast as he=20 -could, he saw the Tortoise had reached the goal, and was comfortably = -dozing=20 -after her fatigue.  
-
 
-
3D"blue
 
Slow but = -steady wins=20 -the race. 
-
 
-
3D"red
- -------=_NextPart_001_000D_01BFC038.5A5C8E60-- - -------=_NextPart_000_000C_01BFC038.5A5C8E60 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-ID: <823504223@17052000-0f8d> - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= - -------=_NextPart_000_000C_01BFC038.5A5C8E60 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-ID: <823504223@17052000-0f94> - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== - -------=_NextPart_000_000C_01BFC038.5A5C8E60-- diff --git a/sources/testing/samples/messages/m0017.txt b/sources/testing/samples/messages/m0017.txt deleted file mode 100644 index a22ff6d..0000000 --- a/sources/testing/samples/messages/m0017.txt +++ /dev/null @@ -1,188 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:47:24 -0400 -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/mixed; - boundary="----=_NextPart_000_0010_01BFC038.B91BC650" -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - -This is a multi-part message in MIME format. - -------=_NextPart_000_0010_01BFC038.B91BC650 -Content-Type: multipart/related; - boundary="----=_NextPart_001_0011_01BFC038.B91BC650" - - -------=_NextPart_001_0011_01BFC038.B91BC650 -Content-Type: multipart/alternative; - boundary="----=_NextPart_002_0012_01BFC038.B91BC650" - - -------=_NextPart_002_0012_01BFC038.B91BC650 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - -The Hare and the Tortoise=20 -=20 -A HARE one day ridiculed the short feet and slow pace of the Tortoise, = -who replied, laughing: "Though you be swift as the wind, I will beat = -you in a race." The Hare, believing her assertion to be simply = -impossible, assented to the proposal; and they agreed that the Fox = -should choose the course and fix the goal. On the day appointed for the = -race the two started together. The Tortoise never for a moment stopped, = -but went on with a slow but steady pace straight to the end of the = -course. The Hare, lying down by the wayside, fell fast asleep. At last = -waking up, and moving as fast as he could, he saw the Tortoise had = -reached the goal, and was comfortably dozing after her fatigue. =20 -=20 -Slow but steady wins the race. =20 - - - -------=_NextPart_002_0012_01BFC038.B91BC650 -Content-Type: text/html; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - - - - -
3D"blue
-

The Hare and the Tortoise = -
 
A HARE=20 -one day ridiculed the short feet and slow pace of the Tortoise, who = -replied,=20 -laughing:  "Though you be swift as the wind, I will beat you in a=20 -race."  The Hare, believing her assertion to be simply impossible, = -assented=20 -to the proposal; and they agreed that the Fox should choose the course = -and fix=20 -the goal.  On the day appointed for the race the two started=20 -together.  The Tortoise never for a moment stopped, but went on = -with a slow=20 -but steady pace straight to the end of the course.  The Hare, lying = -down by=20 -the wayside, fell fast asleep.  At last waking up, and moving as = -fast as he=20 -could, he saw the Tortoise had reached the goal, and was comfortably = -dozing=20 -after her fatigue. 
 
Slow but steady wins the = -race. =20 -
-

 
- -------=_NextPart_002_0012_01BFC038.B91BC650-- - -------=_NextPart_001_0011_01BFC038.B91BC650 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-ID: <938014623@17052000-0f9b> - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= - -------=_NextPart_001_0011_01BFC038.B91BC650-- - -------=_NextPart_000_0010_01BFC038.B91BC650 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= - -------=_NextPart_000_0010_01BFC038.B91BC650 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== - -------=_NextPart_000_0010_01BFC038.B91BC650-- diff --git a/sources/testing/samples/messages/m0018.txt b/sources/testing/samples/messages/m0018.txt deleted file mode 100644 index d56b749..0000000 --- a/sources/testing/samples/messages/m0018.txt +++ /dev/null @@ -1,131 +0,0 @@ -From: "Doug Sauder" -To: "Joe Blow" -Subject: Test message from Microsoft Outlook 00 -Date: Wed, 17 May 2000 19:58:13 -0400 -Message-ID: -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -X-Mailer: Microsoft Outlook IMO, Build 9.0.2416 (9.0.2910.0) -Importance: Normal -X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300 - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, who -replied, laughing: "Though you be swift as the wind, I will beat you in a -race." The Hare, believing her assertion to be simply impossible, assented -to the proposal; and they agreed that the Fox should choose the course and -fix the goal. On the day appointed for the race the two started together. -The Tortoise never for a moment stopped, but went on with a slow but steady -pace straight to the end of the course. The Hare, lying down by the -wayside, fell fast asleep. At last waking up, and moving as fast as he -could, he saw the Tortoise had reached the goal, and was comfortably dozing -after her fatigue. - -Slow but steady wins the race. - - -begin 666 blueball.png -MB5!.1PT*&@H````-24A$4@```!L````;" ,```"Z"@1G```#`%!,5$7___\` -M``@``! ``!@`````""D`$$(`$$H`"#$`$%(((7,(*7L(*80((6L`&%H`"#D( -M(6,0.9P80J480JT80K40.:40,90`&%(0,8PI4K4Y8\8Y8\XI6LX82LX80LX8 -M0KU":\9[G-:\ZMSMZESN]:C.\0,9Q:A-:MUN?.[^^,O>]*>^\8.:482L:, -MO>>UY^^4O>\``"E:C.=SI>]KG.\(*8PQ8^\I6N\((7L````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M``````````````````````````````````````````````````````````#! -M,@U5`````7123E,`0.;89@```!9T15AT4V]F='=AOF@```&(241!5'B<==)M5YLP& ;@$DZ )A!2DX;,"4RG!2+:JHO;K&_; -M?/G_ORA[TE;LL<>;+YQSG3LAY!F-A@0H1"APHYT$.(HA48+#CXS'A*80DK$H -M0=OHT(*,54I*9+0#>J23(LO^U\/RJJ64PV(!L1,[WT[//I^?#)K6MGI -M+#)O>P8QG>P?GO:G9^?SIFP7BC"\*;J$:W%QV??]U8]Y4]7"4I:#>0PB,I'7 -M/_O^U^^;65/60H&%:PMCNKR]OKN_?_"U5B[>S3T66I0'?_[^N_&[U:+3A*_, -MC1QF3Z*=S9]?&D]R:M,L3@9+EW75@%1 "ZLIC_UW>GLLJ)5M65:W-=!2:<+\ -M^59F8J*F==U"28H.B,.2&PMSEJJE%%(N.JOT:U9$9K4D6("++%6VZZRU*O6$ -MUS5_B# 'U$HIG5+"BR@9"(HF'W-"*7TEG(^!UBNNS"&3QP7CG#.X=[-I;7XV -M8))'D!PF`FV3QP`9@XT)W^7M;OUK@. 9P&T-Q6ZVI^E3^8B[X[L#_P$45,S8 -MF6D.OP```"5T15AT0V]M;65N= !C;&EP,F=I9B!V+C N-B!B>2!9=F5S(%!I -49W5E=#9S.[P`````245.1*Y"8((` -` -end - -begin 666 greenball.png -MB5!.1PT*&@H````-24A$4@```!L````;" ,```"Z"@1G```#`%!,5$7___\` -M````$ ``& ``(0``" ``,0``0@``4@``6@``2@`(8P`(P`0C `(:P`` -M.0``8P`0E `0G `AI0`0I0`AK0!"O1ACQC%CQCE2QB$IS@`8O0`0K0`8K0`A -MO0"4UF.MUGN$UE)*WA@AU@`8Q@`8M0``*0!"SA#.YY3>YZW&YY24YV-2YR$A -MW@`8S@!2WB&KRU;5>]/60`]SX,0^'\8HC[&`29]Y('?:!&F+.2< -MAR(@R.NVVF@02C4<#N-8#:LMN[^]G#XU-FDB)VC_PW8#K614(E9K+_6\Z7)DR)5 -M&]$:$C(QV7R9F9)&BH>4:+^V`8\38\HF1ZGD+*AN[TSC4*5)7LIWJN2V#52) -M6-AWSS)*"R>Q(T9Q!/W:?$T$ERJ.E9)RP\KW!$V2%B)"RQ XWX2"D@ZY1A=> -M0!EC0M"@W]+6K ]T1##&U$6K84.VWA,`-4*11EZU$YV]<1^^#V&Y+*TTV=K_ -MJK>-T@@L+ ```"5T15AT0V]M;65N= !C;&EP,F=I -F9B!V+C N-B!B>2!9=F5S(%!I9W5E=#9S.[P`````245.1*Y"8((` -` -end - -begin 666 redball.png -MB5!.1PT*&@H````-24A$4@```!L````;" ,```"Z"@1G```#`%!,5$7___\` -M```!```+```5```:```7```1```*```#```<```R``!$``!-``!(```]```C -M```6```F``!A``![``"&``"'``!]``!T``!@```Y```4```&```G``!+``!O -M``"0``"E``"[``"_``"K``"A``",``!S``!;```N```(``!,``!W``"9``"T -M&1G*.#C5/3W;*2G=!06X``"H``!Y```Q```8```$``!%``":``"[)"3186'? -MA(3F?W_F5E;J'Q_2``"Y``"C``!=``!"```H```)``!G``"Z#P_05%3>DY/E -MK*SIF9GM967O)277``#!``"Q``"<``!M``!3```V```;```%``!*``"!``#+ -M("#=9V?HG)SKIJ;MBHKO45'O%!31``##``"V``!X``!>``! ```B``!7``"2 -M``#"``#:&QOH557L>'CO>'CO5U?O)B;A``#.``"W``"G``"5``!'```K```/ -M``"=``#%``#A!07K*"CO/#SO-37O&QOC``#0``#)``"U``"7``"$``!L``!/ -M```2```"``!B``#I``#O`@+G``#8``#+``"Z``"P``!P```3```D``!8``#( -M``#3``#-``"S``"#``!N```>``!^``# ``"D``"-``!_``!I``!0```P``"1 -M``"I``"\``"J``";``!E``!)```J```.```T``"L``"O``"M``"F``")``!Z -M``!K``!:```^```I``!J``""``"?``">``"6``"/``!\```9```'``!5``". -M``"*```X```0```_``!R``" ``!<```W```L``!F``!#``!6```@```S```\ -M```Z```?```,```=```-```````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M``````````````````````````````````````````````````````````#\ -M+ME%`````7123E,`0.;89@```!9T15AT4V]F='=AOF@```((241!5'B<8V" @_\0P( !F)A96%G9V#DX,:4YN+AY>'GY^+D% -M!#E0)?\S"_$(BXB*B4M(\DH)2?D%A47%R26E9E$=Y1258[C^3D&15=4UM77U#8U.SJWW[10_(IZTQ.W@($E7_+;BD#F\3WZ*P;-F1H[.WSI'D/29P'!J+__\? -MESUQV..DR*)3B[:J;SNM?49(V@(N%\%VEK_#Y=PV^?9=PIE\85.E(^")X_]_ -MCH.R8>=Y0S,OA&JGFV5T6B"EF___#TE?7'M)ZO+E*]P[KQ[O0DY20);%M2FL -MUR]>O7B=[09:<@.QF2QDM-S=M2PP4^)_5(":"/%(84W7`)A(.MC?YL?R```` -M)71%6'1#;VUM96YT`&-L:7 R9VEF('8N,"XV(&)Y(%EV97,@4&EG=65T-G,[ --O ````!)14Y$KD)@@@`` -` -end diff --git a/sources/testing/samples/messages/m0019.txt b/sources/testing/samples/messages/m0019.txt deleted file mode 100644 index 013b125..0000000 --- a/sources/testing/samples/messages/m0019.txt +++ /dev/null @@ -1,3 +0,0 @@ -Date: Sat, 28 Feb 2015 22:32:35 +0100 -MIME-Version: 1.0 -Subject: =?windows-1252?Q?Te=E9st?= diff --git a/sources/testing/samples/messages/m1001.txt b/sources/testing/samples/messages/m1001.txt deleted file mode 100644 index a5b8214..0000000 --- a/sources/testing/samples/messages/m1001.txt +++ /dev/null @@ -1,32 +0,0 @@ -Message-ID: <3923561C.B7078DEF@example.com> -Date: Wed, 17 May 2000 22:31:57 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: =?iso-8859-1?Q?J=FCrgen=20Schm=FCrgen?= -Subject: Die Hasen und die =?iso-8859-1?Q?Fr=F6sche?= (Netscape Communicator 4.7) -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - - diff --git a/sources/testing/samples/messages/m1002.txt b/sources/testing/samples/messages/m1002.txt deleted file mode 100644 index 15a8d34..0000000 --- a/sources/testing/samples/messages/m1002.txt +++ /dev/null @@ -1,61 +0,0 @@ -Message-ID: <392359CF.DFF4527@example.com> -Date: Wed, 17 May 2000 22:47:43 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Heinz =?iso-8859-1?Q?M=FCller?= -Subject: Die Hasen und die =?iso-8859-1?Q?Fr=F6sche?= (Netscape Communicator 4.7) -Content-Type: multipart/alternative; - boundary="------------9D454F23DA86BCD63FA3805F" - - ---------------9D454F23DA86BCD63FA3805F -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - - - ---------------9D454F23DA86BCD63FA3805F -Content-Type: text/html; charset=us-ascii -Content-Transfer-Encoding: 7bit - - - -Die Hasen und die Frösche -

Die Hasen klagten einst über ihre mißliche Lage; "wir leben", -sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute -der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger -als der Tod selbst. Auf, laßt uns ein für allemal sterben." -

In einem nahen Teich wollten sie sich nun ersäufen; sie eilten -ihm zu; allein das außerordentliche Getöse und ihre wunderbare -Gestalt erschreckte eine Menge Frösche, die am Ufer saßen, so -sehr, daß sie aufs schnellste untertauchten. -

"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen -noch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht, -einige Tiere, welche also wohl noch unglücklicher sein müssen -als wir." -
  -
  - ---------------9D454F23DA86BCD63FA3805F-- diff --git a/sources/testing/samples/messages/m1003.txt b/sources/testing/samples/messages/m1003.txt deleted file mode 100644 index d64bf91..0000000 --- a/sources/testing/samples/messages/m1003.txt +++ /dev/null @@ -1,137 +0,0 @@ -Message-ID: <39235E1C.1DC7EA90@example.com> -Date: Wed, 17 May 2000 23:06:04 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------A1FCDEE154E03D875E5D6779" - -This is a multi-part message in MIME format. ---------------A1FCDEE154E03D875E5D6779 -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - - - ---------------A1FCDEE154E03D875E5D6779 -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV -AAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH -AAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb -AAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa -AAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr -pqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv -V1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ -AADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT -AAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+ -AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA -AABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII -SURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ -Scsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja -2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/ -uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW -0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw -bNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8 -YVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t -ZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---------------A1FCDEE154E03D875E5D6779 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA -IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh -rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC -zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY -1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp -1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt -SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b -Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ -aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV -G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex -I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr -YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0 -RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C -YII= ---------------A1FCDEE154E03D875E5D6779 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------A1FCDEE154E03D875E5D6779-- diff --git a/sources/testing/samples/messages/m1004.txt b/sources/testing/samples/messages/m1004.txt deleted file mode 100644 index 266fd19..0000000 --- a/sources/testing/samples/messages/m1004.txt +++ /dev/null @@ -1,133 +0,0 @@ -Message-ID: <39235EAD.E48E2160@example.com> -Date: Wed, 17 May 2000 23:08:29 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------C78F594988075E36AE03C243" - -This is a multi-part message in MIME format. ---------------C78F594988075E36AE03C243 -Content-Type: multipart/alternative; - boundary="------------D74AE2393FB01D1B284AE257" - - ---------------D74AE2393FB01D1B284AE257 -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - - - ---------------D74AE2393FB01D1B284AE257 -Content-Type: text/html; charset=us-ascii -Content-Transfer-Encoding: 7bit - - - -Die Hasen und die Frösche -

Die Hasen klagten einst über ihre mißliche Lage; "wir leben", -sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute -der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger -als der Tod selbst. Auf, laßt uns ein für allemal sterben." -

In einem nahen Teich wollten sie sich nun ersäufen; sie eilten -ihm zu; allein das außerordentliche Getöse und ihre wunderbare -Gestalt erschreckte eine Menge Frösche, die am Ufer saßen, so -sehr, daß sie aufs schnellste untertauchten. -

"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen -noch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht, -einige Tiere, welche also wohl noch unglücklicher sein müssen -als wir." -
  -
  - ---------------D74AE2393FB01D1B284AE257-- - ---------------C78F594988075E36AE03C243 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA -IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh -rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC -zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY -1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp -1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt -SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b -Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ -aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV -G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex -I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr -YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0 -RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C -YII= ---------------C78F594988075E36AE03C243 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------C78F594988075E36AE03C243-- - diff --git a/sources/testing/samples/messages/m1005.txt b/sources/testing/samples/messages/m1005.txt deleted file mode 100644 index 8825c16..0000000 --- a/sources/testing/samples/messages/m1005.txt +++ /dev/null @@ -1,212 +0,0 @@ -Message-ID: <39235FC5.276CCE00@example.com> -Date: Wed, 17 May 2000 23:13:09 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Heinz =?iso-8859-1?Q?M=FCller?= -Subject: Die Hasen und die =?iso-8859-1?Q?Fr=F6sche?= (Netscape Messenger 4.7) -Content-Type: multipart/mixed; - boundary="------------A1E83A41894D3755390B838A" - -This is a multi-part message in MIME format. ---------------A1E83A41894D3755390B838A -Content-Type: multipart/alternative; - boundary="------------F03F94BA73D3B9E8C1B94D92" - - ---------------F03F94BA73D3B9E8C1B94D92 -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -[blue ball] - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - -[Image] - - - ---------------F03F94BA73D3B9E8C1B94D92 -Content-Type: multipart/related; - boundary="------------C02FA3D0A04E95F295FB25EB" - - ---------------C02FA3D0A04E95F295FB25EB -Content-Type: text/html; charset=us-ascii -Content-Transfer-Encoding: 7bit - - - -blue ball -

Die Hasen und die Frösche -

Die Hasen klagten einst über ihre mißliche Lage; "wir leben", -sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute -der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger -als der Tod selbst. Auf, laßt uns ein für allemal sterben." -

In einem nahen Teich wollten sie sich nun ersäufen; sie eilten -ihm zu; allein das außerordentliche Getöse und ihre wunderbare -Gestalt erschreckte eine Menge Frösche, die am Ufer saßen, so -sehr, daß sie aufs schnellste untertauchten. -

"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen -noch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht, -einige Tiere, welche also wohl noch unglücklicher sein müssen -als wir." -

-
  -
  - ---------------C02FA3D0A04E95F295FB25EB -Content-Type: image/png -Content-ID: -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="C:\TEMP\nsmailEG.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------C02FA3D0A04E95F295FB25EB -Content-Type: image/png -Content-ID: -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="C:\TEMP\nsmail39.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV -AAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH -AAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb -AAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa -AAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr -pqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv -V1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ -AADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT -AAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+ -AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA -AABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII -SURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ -Scsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja -2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/ -uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW -0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw -bNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8 -YVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t -ZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---------------C02FA3D0A04E95F295FB25EB-- - ---------------F03F94BA73D3B9E8C1B94D92-- - ---------------A1E83A41894D3755390B838A -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV -AAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH -AAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb -AAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa -AAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr -pqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv -V1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ -AADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT -AAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+ -AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA -AABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII -SURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ -Scsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja -2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/ -uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW -0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw -bNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8 -YVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t -ZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---------------A1E83A41894D3755390B838A -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA -IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh -rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC -zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY -1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp -1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt -SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b -Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ -aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV -G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex -I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr -YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0 -RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C -YII= ---------------A1E83A41894D3755390B838A-- - diff --git a/sources/testing/samples/messages/m1006.txt b/sources/testing/samples/messages/m1006.txt deleted file mode 100644 index bd31d2e..0000000 --- a/sources/testing/samples/messages/m1006.txt +++ /dev/null @@ -1,171 +0,0 @@ -Message-ID: <39236103.FFE674FC@example.com> -Date: Wed, 17 May 2000 23:18:27 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: =?iso-8859-1?Q?J=FCrgen=20Schm=FCrgen?= -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------B7133A01A6B323BF00DBC9A7" - -This is a multi-part message in MIME format. ---------------B7133A01A6B323BF00DBC9A7 -Content-Type: multipart/related; - boundary="------------8E6A06810565BCAB5E1F7D97" - - ---------------8E6A06810565BCAB5E1F7D97 -Content-Type: text/html; charset=us-ascii -Content-Transfer-Encoding: 7bit - - - -blue ball -

Die Hasen und die Frösche -

Die Hasen klagten einst über ihre mißliche Lage; "wir leben", -sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute -der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger -als der Tod selbst. Auf, laßt uns ein für allemal sterben." -

In einem nahen Teich wollten sie sich nun ersäufen; sie eilten -ihm zu; allein das außerordentliche Getöse und ihre wunderbare -Gestalt erschreckte eine Menge Frösche, die am Ufer saßen, so -sehr, daß sie aufs schnellste untertauchten. -

"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen -noch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht, -einige Tiere, welche also wohl noch unglücklicher sein müssen -als wir." -

red ball - ---------------8E6A06810565BCAB5E1F7D97 -Content-Type: image/png -Content-ID: -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="C:\TEMP\nsmailV0.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------8E6A06810565BCAB5E1F7D97 -Content-Type: image/png -Content-ID: -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="C:\TEMP\nsmailNM.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAV -AAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACH -AAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABb -AAAuAAAIAABMAAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACa -AAC7JCTRYWHfhITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzr -pqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjv -V1fvJibhAADOAAC3AACnAACVAABHAAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQ -AADJAAC1AACXAACEAABsAABPAAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAAT -AAAkAABYAADIAADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6AABrAABaAAA+ -AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACOAACKAAA4AAAQAAA/AAByAACA -AABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAII -SURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iKiUtI8koJ -Scsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja -2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/ -uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW -0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36Kw -bNmRo7O3zpHkPSZwHBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8 -YVOlI+CJ4/9/joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21t -ZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---------------8E6A06810565BCAB5E1F7D97-- - ---------------B7133A01A6B323BF00DBC9A7 -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA -IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh -rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC -zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY -1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp -1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt -SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b -Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ -aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV -G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex -I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr -YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0 -RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C -YII= ---------------B7133A01A6B323BF00DBC9A7 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------B7133A01A6B323BF00DBC9A7-- - diff --git a/sources/testing/samples/messages/m1007.txt b/sources/testing/samples/messages/m1007.txt deleted file mode 100644 index f30e8dc..0000000 --- a/sources/testing/samples/messages/m1007.txt +++ /dev/null @@ -1,17 +0,0 @@ -Message-ID: <3923625B.59F0C3AC@example.com> -Date: Wed, 17 May 2000 23:24:11 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, who replied, laughing: "Though you be swift as the wind, I will beat you in a race." The Hare, believing her assertion to be simply impossible, assented to the proposal; and they agreed that the Fox should choose the course and fix the goal. On the day appointed for the race the two started together. The Tortoise never for a moment stopped, but went on with a slow but steady pace straight to the end of the course. The Hare, lying down by the wayside, fell fast asleep. At last waking up, and moving as fast as he could, he saw the Tortoise had reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. diff --git a/sources/testing/samples/messages/m1008.txt b/sources/testing/samples/messages/m1008.txt deleted file mode 100644 index 572f3ce..0000000 --- a/sources/testing/samples/messages/m1008.txt +++ /dev/null @@ -1,27 +0,0 @@ -Message-ID: <392362D8.B650789F@example.com> -Date: Wed, 17 May 2000 23:26:16 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -X-Priority: 1 (Highest) -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, -who replied, laughing: "Though you be swift as the wind, I will beat -you in a race." The Hare, believing her assertion to be simply -impossible, assented to the proposal; and they agreed that the Fox -should choose the course and fix the goal. On the day appointed for the -race the two started together. The Tortoise never for a moment stopped, -but went on with a slow but steady pace straight to the end of the -course. The Hare, lying down by the wayside, fell fast asleep. At last -waking up, and moving as fast as he could, he saw the Tortoise had -reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. diff --git a/sources/testing/samples/messages/m1009.txt b/sources/testing/samples/messages/m1009.txt deleted file mode 100644 index 2b0db85..0000000 --- a/sources/testing/samples/messages/m1009.txt +++ /dev/null @@ -1,111 +0,0 @@ -Message-ID: <3923635B.85C58444@example.com> -Date: Wed, 17 May 2000 23:28:27 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------0BBC657DDC74A0B8454627FD" - -This is a multi-part message in MIME format. ---------------0BBC657DDC74A0B8454627FD -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, -who replied, laughing: "Though you be swift as the wind, I will beat -you in a race." The Hare, believing her assertion to be simply -impossible, assented to the proposal; and they agreed that the Fox -should choose the course and fix the goal. On the day appointed for the -race the two started together. The Tortoise never for a moment stopped, -but went on with a slow but steady pace straight to the end of the -course. The Hare, lying down by the wayside, fell fast asleep. At last -waking up, and moving as fast as he could, he saw the Tortoise had -reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. ---------------0BBC657DDC74A0B8454627FD -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------0BBC657DDC74A0B8454627FD -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAA -IQAACAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAh -rQBCvRhjxjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBC -zhDO55Te563G55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY -1gAx5wBS7yFr7zlK7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp -1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAFt -SURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy+N9ery1b -Ve9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh0fHJ -aTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJV -G9EaEjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyex -I0ZxBP3afE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWr -YUO23hMANUKRRl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0 -RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5C -YII= ---------------0BBC657DDC74A0B8454627FD -Content-Type: text/plain; charset=us-ascii; - name="hareandtoroise.txt" -Content-Transfer-Encoding: 7bit -Content-Disposition: inline; - filename="hareandtoroise.txt" - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, who replied, laughing: "Though you be swift as the wind, I will beat you in a race." The Hare, believing her assertion to be simply impossible, assented to the proposal; and they agreed that the Fox should choose the course and fix the goal. On the day appointed for the race the two started together. The Tortoise never for a moment stopped, but went on with a slow but steady pace straight to the end of the course. The Hare, lying down by the wayside, fell fast asleep. At last waking up, and moving as fast as he could, he saw the Tortoise had reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. - ---------------0BBC657DDC74A0B8454627FD-- - diff --git a/sources/testing/samples/messages/m1010.txt b/sources/testing/samples/messages/m1010.txt deleted file mode 100644 index f84c55d..0000000 --- a/sources/testing/samples/messages/m1010.txt +++ /dev/null @@ -1,33 +0,0 @@ -Message-ID: <392363E1.2AC47AB3@example.com> -Date: Wed, 17 May 2000 23:30:41 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -X-Priority: 5 (Lowest) -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als de= -r -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." = - - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs -schnellste untertauchten. = - - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." diff --git a/sources/testing/samples/messages/m1011.txt b/sources/testing/samples/messages/m1011.txt deleted file mode 100644 index b1d2a62..0000000 --- a/sources/testing/samples/messages/m1011.txt +++ /dev/null @@ -1,26 +0,0 @@ -Message-ID: <39236497.12FA2A89@example.com> -Date: Wed, 17 May 2000 23:33:43 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: "Heinz M+APw-ller" -Subject: Die Hasen und die Fr+APY-sche -Content-Type: text/plain; charset=UTF-7 -Content-Transfer-Encoding: 7bit - -Die Hasen und die Fr+XKM-he - -Die Hasen klagten einst +uGU-r ihre mi+B6w-iche Lage; "wir leben", sprach ein -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist +PGc-er als der -Tod selbst. Auf, la+B7Q- uns ein f+vCA-allemal sterben." - -In einem nahen Teich wollten sie sich nun ers+PSY-en; sie eilten ihm zu; -allein das au+B6U-rordentliche Get+XKU- und ihre wunderbare Gestalt -erschreckte eine Menge Fr+XKM-he, die am Ufer sa+B6U-n, so sehr, da+B2A-sie aufs -schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers+PSY-en noch ein -wenig aufschieben, denn auch uns f+vGM-hten, wie ihr seht, einige Tiere, -welche also wohl noch ungl+uKs-licher sein m+vLM-en als wir." diff --git a/sources/testing/samples/messages/m1012.txt b/sources/testing/samples/messages/m1012.txt deleted file mode 100644 index cd04d23..0000000 --- a/sources/testing/samples/messages/m1012.txt +++ /dev/null @@ -1,28 +0,0 @@ -Message-ID: <39236653.CAA8A090@example.com> -Date: Wed, 17 May 2000 23:41:07 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: =?iso-8859-1?Q?J=FCrgen=20Schm=FCrgen?= -Subject: Die Hasen und die =?iso-8859-1?Q?Fr=F6sche?= -Content-Type: text/plain; charset=iso-8859-1 -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hun= -de, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger al= -s der Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." = - - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; = -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt erschr= -eckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie aufs = -schnellste untertauchten. = - - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere= -, welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." diff --git a/sources/testing/samples/messages/m1013.txt b/sources/testing/samples/messages/m1013.txt deleted file mode 100644 index 027f4b8..0000000 --- a/sources/testing/samples/messages/m1013.txt +++ /dev/null @@ -1,51 +0,0 @@ -Message-ID: <392366A7.21929EA7@example.com> -Date: Wed, 17 May 2000 23:42:31 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------E7889DDF0F75D34163841C59" - -This is a multi-part message in MIME format. ---------------E7889DDF0F75D34163841C59 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - ---------------E7889DDF0F75D34163841C59 -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: inline; - filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgA -AAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQ -MZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYY -QsYQMaUAACHO5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9K -e+8YOaUYSsaMvee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAuMT1evmgAAAGI -SURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbsscebL5xznTsh -5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqW -Uw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1C -SYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIom -H3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0N -xW62p+lT+Yi747sD/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBi -eSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---------------E7889DDF0F75D34163841C59-- - diff --git a/sources/testing/samples/messages/m1014.txt b/sources/testing/samples/messages/m1014.txt deleted file mode 100644 index 4ea7a65..0000000 --- a/sources/testing/samples/messages/m1014.txt +++ /dev/null @@ -1,155 +0,0 @@ -Message-ID: <3923670C.67D83A99@example.com> -Date: Wed, 17 May 2000 23:44:12 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------177483472E7788F4AD46AB1A" - -This is a multi-part message in MIME format. ---------------177483472E7788F4AD46AB1A -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, -who replied, laughing: "Though you be swift as the wind, I will beat -you in a race." The Hare, believing her assertion to be simply -impossible, assented to the proposal; and they agreed that the Fox -should choose the course and fix the goal. On the day appointed for the -race the two started together. The Tortoise never for a moment stopped, -but went on with a slow but steady pace straight to the end of the -course. The Hare, lying down by the wayside, fell fast asleep. At last -waking up, and moving as fast as he could, he saw the Tortoise had -reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. ---------------177483472E7788F4AD46AB1A -Content-Type: image/png; - name="redball.png" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: inline; - filename="redball.png" - -begin 644 redball.png -MB5!.1PT*&@H -24A$4@ !L ;" , "Z"@1G # %!,5$7___\ -M ! + 5 : 7 1 * # < R !$ !- !( ] C -M 6 F !A ![ "& "' !] !T !@ Y 4 & G !+ !O -M "0 "E "[ "_ "K "A ", !S !; N ( !, !W "9 "T -M&1G*.#C5/3W;*2G=!06X "H !Y Q 8 $ !% ": "[)"3186'? -MA(3F?W_F5E;J'Q_2 "Y "C != !" H ) !G "Z#P_05%3>DY/E -MK*SIF9GM967O)277 #! "Q "< !M !3 V ; % !* "! #+ -M("#=9V?HG)SKIJ;MBHKO45'O%!31 ## "V !X !> ! B !7 "2 -M #" #:&QOH557L>'CO>'CO5U?O)B;A #. "W "G "5 !' K / -M "= #% #A!07K*"CO/#SO-37O&QOC #0 #) "U "7 "$ !L !/ -M 2 " !B #I #O @+G #8 #+ "Z "P !P 3 D !8 #( -M #3 #- "S "# !N > !^ # "D "- !_ !I !0 P "1 -M "I "\ "J "; !E !) J . T "L "O "M "F ") !Z -M !K !: ^ I !J "" "? "> "6 "/ !\ 9 ' !5 ". -M "* X 0 _ !R " !< W L !F !# !6 @ S \ -M Z ? , = - -M -M #\ -M+ME% 7123E, 0.;89@ !9T15AT4V]F='=AOF@ ((241!5'B<8V" @_\0P( !F)A96%G9V#DX,:4YN+AY>'GY^+D% -M!#E0)?\S"_$(BXB*B4M(\DH)2?D%A47%R26E9E$=Y1258[C^3D&15=4UM77U#8U.SJWW[10_(IZTQ.W@($E7_+;BD#F\3WZ*P;-F1H[.WSI'D/29P'!J+__\? -MESUQV..DR*)3B[:J;SNM?49(V@(N%\%VEK_#Y=PV^?9=PIE\85.E(^")X_]_ -MCH.R8>=Y0S,OA&JGFV5T6B"EF___#TE?7'M)ZO+E*]P[KQ[O0DY20);%M2FL -MUR]>O7B=[09:<@.QF2QDM-S=M2PP4^)_5(":"/%(84W7 )A(.MC?YL?R -M)71%6'1#;VUM96YT &-L:7 R9VEF('8N,"XV(&)Y(%EV97,@4&EG=65T-G,[ --O !)14Y$KD)@@@ -end - ---------------177483472E7788F4AD46AB1A -Content-Type: image/png; - name="greenball.png" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: inline; - filename="greenball.png" - -begin 644 greenball.png -MB5!.1PT*&@H -24A$4@ !L ;" , "Z"@1G # %!,5$7___\ -M $ & (0 " ,0 0@ 4@ 6@ 2@ (8P (

P 0C (:P -M.0 8P 0E 0G AI0 0I0 AK0!"O1ACQC%CQCE2QB$IS@ 8O0 0K0 8K0 A -MO0"4UF.MUGN$UE)*WA@AU@ 8Q@ 8M0 *0!"SA#.YY3>YZW&YY24YV-2YR$A -MW@ 8S@!2WB&KRU;5>]/60 ]SX,0^'\8HC[& 29]Y('?:!&F+.2< -MAR(@R.NVVF@02C4<#N-8#:LMN[^]G#XU-FDB)VC_PW8#K614(E9K+_6\Z7)DR)5 -M&]$:$C(QV7R9F9)&BH>4:+^V 8\38\HF1ZGD+*AN[TSC4*5)7LIWJN2V#52) -M6-AWSS)*"R>Q(T9Q!/W:?$T$ERJ.E9)RP\KW!$V2%B)"RQ XWX2"D@ZY1A=> -M0!EC0M"@W]+6K ]T1##&U$6K84.VWA, -4*11EZU$YV]<1^^#V&Y+*TTV=K_ -MJK>-T@@L+ "5T15AT0V]M;65N= !C;&EP,F=I -F9B!V+C N-B!B>2!9=F5S(%!I9W5E=#9S.[P 245.1*Y"8(( -end - ---------------177483472E7788F4AD46AB1A -Content-Type: image/png; - name="blueball.png" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: inline; - filename="blueball.png" - -begin 644 blueball.png -MB5!.1PT*&@H -24A$4@ !L ;" , "Z"@1G # %!,5$7___\ -M @ ! !@ ""D $$( $$H "#$ $%(((7,(*7L(*80((6L &%H "#D( -M(6,0.9P80J480JT80K40.:40,90 &%(0,8PI4K4Y8\8Y8\XI6LX82LX80LX8 -M0KU":\9[G-:\ZMSMZESN]:C.\0,9Q:A-:MUN?.[^^,O>]*>^\8.:482L:, -MO>>UY^^4O>\ "E:C.=SI>]KG.\(*8PQ8^\I6N\((7L -M -M -M -M -M -M -M -M -M -M -M -M #! -M,@U5 7123E, 0.;89@ !9T15AT4V]F='=AOF@ &(241!5'B<==)M5YLP& ;@$DZ )A!2DX;,"4RG!2+:JHO;K&_; -M?/G_ORA[TE;LL<>;+YQSG3LAY!F-A@0H1"APHYT$.(HA48+#CXS'A*80DK$H -M0=OHT(*,54I*9+0#>J23(LO^U\/RJJ64PV(!L1,[WT[//I^?#)K6MGI -M+#)O>P8QG>P?GO:G9^?SIFP7BC"\*;J$:W%QV??]U8]Y4]7"4I:#>0PB,I'7 -M/_O^U^^;65/60H&%:PMCNKR]OKN_?_"U5B[>S3T66I0'?_[^N_&[U:+3A*_, -MC1QF3Z*=S9]?&D]R:M,L3@9+EW75@%1 "ZLIC_UW>GLLJ)5M65:W-=!2:<+\ -M^59F8J*F==U"28H.B,.2&PMSEJJE%%(N.JOT:U9$9K4D6("++%6VZZRU*O6$ -MUS5_B# 'U$HIG5+"BR@9"(HF'W-"*7TEG(^!UBNNS"&3QP7CG#.X=[-I;7XV -M8))'D!PF FV3QP 9@XT)W^7M;OUK@. 9P&T-Q6ZVI^E3^8B[X[L#_P$45,S8 -MF6D.OP "5T15AT0V]M;65N= !C;&EP,F=I9B!V+C N-B!B>2!9=F5S(%!I -49W5E=#9S.[P 245.1*Y"8(( -end - ---------------177483472E7788F4AD46AB1A-- - diff --git a/sources/testing/samples/messages/m1015.txt b/sources/testing/samples/messages/m1015.txt deleted file mode 100644 index ccc7d8d..0000000 --- a/sources/testing/samples/messages/m1015.txt +++ /dev/null @@ -1,60 +0,0 @@ -Message-ID: <392367BC.3D075C95@example.com> -Date: Wed, 17 May 2000 23:47:08 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------CA611088711119FBDB3473B4" - -This is a multi-part message in MIME format. ---------------CA611088711119FBDB3473B4 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, -who replied, laughing: "Though you be swift as the wind, I will beat -you in a race." The Hare, believing her assertion to be simply -impossible, assented to the proposal; and they agreed that the Fox -should choose the course and fix the goal. On the day appointed for the -race the two started together. The Tortoise never for a moment stopped, -but went on with a slow but steady pace straight to the end of the -course. The Hare, lying down by the wayside, fell fast asleep. At last -waking up, and moving as fast as he could, he saw the Tortoise had -reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. ---------------CA611088711119FBDB3473B4 -Content-Type: text/plain; charset=iso-8859-1; - name="=?iso-8859-1?Q?HasenundFr=F6sche=2Etxt?=" -Content-Transfer-Encoding: quoted-printable -Content-Disposition: inline; - filename="=?iso-8859-1?Q?HasenundFr=F6sche=2Etxt?=" - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach = -ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hun= -de, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger al= -s der Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." = - - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; = -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt erschr= -eckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie aufs = -schnellste untertauchten. = - - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ei= -n wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere= -, welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." = - - - ---------------CA611088711119FBDB3473B4-- - diff --git a/sources/testing/samples/messages/m1016.txt b/sources/testing/samples/messages/m1016.txt deleted file mode 100644 index b4667c2..0000000 --- a/sources/testing/samples/messages/m1016.txt +++ /dev/null @@ -1,58 +0,0 @@ -Message-ID: <3923686C.99FB5E36@example.com> -Date: Wed, 17 May 2000 23:50:04 -0400 -From: Doug Sauder -X-Mailer: Mozilla 4.7 [en] (WinNT; I) -X-Accept-Language: en -MIME-Version: 1.0 -To: Joe Blow -Subject: Test message from Netscape Communicator 4.7 -Content-Type: multipart/mixed; - boundary="------------77060D866A66DC8D0921E051" - -This is a multi-part message in MIME format. ---------------77060D866A66DC8D0921E051 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, -who replied, laughing: "Though you be swift as the wind, I will beat -you in a race." The Hare, believing her assertion to be simply -impossible, assented to the proposal; and they agreed that the Fox -should choose the course and fix the goal. On the day appointed for the -race the two started together. The Tortoise never for a moment stopped, -but went on with a slow but steady pace straight to the end of the -course. The Hare, lying down by the wayside, fell fast asleep. At last -waking up, and moving as fast as he could, he saw the Tortoise had -reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. ---------------77060D866A66DC8D0921E051 -Content-Type: text/plain; charset=us-ascii; - name="farmerandstork.txt" -Content-Transfer-Encoding: 7bit -Content-Disposition: inline; - filename="farmerandstork.txt" - - -The Farmer and the Stork - -A FARMER placed nets on his newly-sown plowlands and caught a -number of Cranes, which came to pick up his seed. With them he -trapped a Stork that had fractured his leg in the net and was -earnestly beseeching the Farmer to spare his life. "Pray save -me, Master," he said, "and let me go free this once. My broken -limb should excite your pity. Besides, I am no Crane, I am a -Stork, a bird of excellent character; and see how I love and -slave for my father and mother. Look too, at my feathers-- -they are not the least like those of a Crane." The Farmer -laughed aloud and said, "It may be all as you say, I only know -this: I have taken you with these robbers, the Cranes, and you -must die in their company." - -Birds of a feather flock together. - ---------------77060D866A66DC8D0921E051-- - diff --git a/sources/testing/samples/messages/m2001.txt b/sources/testing/samples/messages/m2001.txt deleted file mode 100644 index d21c893..0000000 --- a/sources/testing/samples/messages/m2001.txt +++ /dev/null @@ -1,29 +0,0 @@ -Message-Id: <4.2.0.58.20000519001217.00a85b60@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -Date: Fri, 19 May 2000 00:17:39 -0400 -To: =?iso-8859-1?Q?J=FCrgen?= =?iso-8859-1?Q?_Schm=FCrgen?= - -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?= -Mime-Version: 1.0 -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - diff --git a/sources/testing/samples/messages/m2002.txt b/sources/testing/samples/messages/m2002.txt deleted file mode 100644 index 16a779e..0000000 --- a/sources/testing/samples/messages/m2002.txt +++ /dev/null @@ -1,63 +0,0 @@ -Message-Id: <4.2.0.58.20000519002557.00a88870@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:29:55 -0400 -To: Heinz =?iso-8859-1?Q?M=FCller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?= -Mime-Version: 1.0 -Content-Type: multipart/alternative; - boundary="=====================_714967308==_.ALT" - ---=====================_714967308==_.ALT -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - ---=====================_714967308==_.ALT -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-
- ---=====================_714967308==_.ALT-- - diff --git a/sources/testing/samples/messages/m2003.txt b/sources/testing/samples/messages/m2003.txt deleted file mode 100644 index dc0e99d..0000000 --- a/sources/testing/samples/messages/m2003.txt +++ /dev/null @@ -1,36 +0,0 @@ -Message-Id: <4.2.0.58.20000519003052.00a89c40@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:31:00 -0400 -To: Heinz =?iso-8859-1?Q?M=FCller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?= -Mime-Version: 1.0 -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-
- diff --git a/sources/testing/samples/messages/m2004.txt b/sources/testing/samples/messages/m2004.txt deleted file mode 100644 index a73ab64..0000000 --- a/sources/testing/samples/messages/m2004.txt +++ /dev/null @@ -1,138 +0,0 @@ -Message-Id: <4.2.0.58.20000519003143.00a8d550@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:33:01 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/related; - type="multipart/alternative"; - boundary="=====================_715157131==_.REL" - ---=====================_715157131==_.REL -Content-Type: multipart/alternative; - boundary="=====================_715157141==_.ALT" - ---=====================_715157141==_.ALT -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -blueball.png - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - -redball.png ---=====================_715157141==_.ALT -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -3D"blueball.png"
-
-Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-3D"redball.png"
- ---=====================_715157141==_.ALT-- - ---=====================_715157131==_.REL -Content-Type: image/png; name="blueball.png" -Content-ID: <4.2.0.58.20000519003143.00a8d550@pop.example.com.0> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715157131==_.REL -Content-Type: image/png; name="redball.png" -Content-ID: <4.2.0.58.20000519003143.00a8d550@pop.example.com.1> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715157131==_.REL-- - diff --git a/sources/testing/samples/messages/m2005.txt b/sources/testing/samples/messages/m2005.txt deleted file mode 100644 index 4a11dfe..0000000 --- a/sources/testing/samples/messages/m2005.txt +++ /dev/null @@ -1,198 +0,0 @@ -Message-Id: <4.2.0.58.20000519003556.00a918e0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:36:58 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_715392540==_" - ---=====================_715392540==_ -Content-Type: multipart/related; - type="multipart/alternative"; - boundary="=====================_715392540==_.REL" - ---=====================_715392540==_.REL -Content-Type: multipart/alternative; - boundary="=====================_715392550==_.ALT" - ---=====================_715392550==_.ALT -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - -2aa3ed95.png2aa3edd1.png ---=====================_715392550==_.ALT -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-
3D"2aa3ed95.png" - ---=====================_715392550==_.ALT-- - ---=====================_715392540==_.REL -Content-Type: image/png; name="2aa3ed95.png" -Content-ID: <4.2.0.58.20000519003556.00a918e0@pop.example.com.2> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa3ed95.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715392540==_.REL -Content-Type: image/png; name="2aa3edd1.png" -Content-ID: <4.2.0.58.20000519003556.00a918e0@pop.example.com.3> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa3edd1.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715392540==_.REL-- - ---=====================_715392540==_ -Content-Type: image/png; name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715392540==_ -Content-Type: image/png; name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---=====================_715392540==_-- - diff --git a/sources/testing/samples/messages/m2006.txt b/sources/testing/samples/messages/m2006.txt deleted file mode 100644 index cc76017..0000000 --- a/sources/testing/samples/messages/m2006.txt +++ /dev/null @@ -1,109 +0,0 @@ -Message-Id: <4.2.0.58.20000519003735.00a8d7e0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:37:39 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/related; - type="text/html"; - boundary="=====================_715429112==_.REL" - ---=====================_715429112==_.REL -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -3D"2aa48eb6.png"
-
-Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-3D"2aa48ec0.png"
- ---=====================_715429112==_.REL -Content-Type: image/png; name="2aa48eb6.png" -Content-ID: <4.2.0.58.20000519003735.00a8d7e0@pop.example.com.2> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa48eb6.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715429112==_.REL -Content-Type: image/png; name="2aa48ec0.png" -Content-ID: <4.2.0.58.20000519003735.00a8d7e0@pop.example.com.3> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa48ec0.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715429112==_.REL-- - diff --git a/sources/testing/samples/messages/m2007.txt b/sources/testing/samples/messages/m2007.txt deleted file mode 100644 index dfb9f4a..0000000 --- a/sources/testing/samples/messages/m2007.txt +++ /dev/null @@ -1,171 +0,0 @@ -Message-Id: <4.2.0.58.20000519003809.00a85140@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:38:13 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_715462801==_" - ---=====================_715462801==_ -Content-Type: multipart/related; - type="text/html"; - boundary="=====================_715462801==_.REL" - ---=====================_715462801==_.REL -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-
3D"2aa51367.png" - ---=====================_715462801==_.REL -Content-Type: image/png; name="2aa51367.png" -Content-ID: <4.2.0.58.20000519003809.00a85140@pop.example.com.2> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa51367.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715462801==_.REL -Content-Type: image/png; name="2aa51371.png" -Content-ID: <4.2.0.58.20000519003809.00a85140@pop.example.com.3> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa51371.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715462801==_.REL-- - ---=====================_715462801==_ -Content-Type: image/png; name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715462801==_ -Content-Type: image/png; name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---=====================_715462801==_-- - diff --git a/sources/testing/samples/messages/m2008.txt b/sources/testing/samples/messages/m2008.txt deleted file mode 100644 index bd1df32..0000000 --- a/sources/testing/samples/messages/m2008.txt +++ /dev/null @@ -1,161 +0,0 @@ -Message-Id: <4.2.0.58.20000519003903.00a859b0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:39:05 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_715515186==_" - ---=====================_715515186==_ -Content-Type: multipart/related; - type="text/plain"; - boundary="=====================_715515186==_.REL" - ---=====================_715515186==_.REL -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - -2aa5e03a.png2aa5e044.png ---=====================_715515186==_.REL -Content-Type: image/png; name="2aa5e03a.png" -Content-ID: <4.2.0.58.20000519003903.00a859b0@pop.example.com.2> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa5e03a.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715515186==_.REL -Content-Type: image/png; name="2aa5e044.png" -Content-ID: <4.2.0.58.20000519003903.00a859b0@pop.example.com.3> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa5e044.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715515186==_.REL-- - ---=====================_715515186==_ -Content-Type: image/png; name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715515186==_ -Content-Type: image/png; name="greenball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="greenball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAAAEAAAGAAAIQAA -CAAAMQAAQgAAUgAAWgAASgAIYwAIcwAIewAQjAAIawAAOQAAYwAQlAAQnAAhpQAQpQAhrQBCvRhj -xjFjxjlSxiEpzgAYvQAQrQAYrQAhvQCU1mOt1nuE1lJK3hgh1gAYxgAYtQAAKQBCzhDO55Te563G -55SU52NS5yEh3gAYzgBS3iGc52vW75y974yE71JC7xCt73ul3nNa7ykh5wAY1gAx5wBS7yFr7zlK -7xgp5wAp7wAx7wAIhAAQtQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAp1fnZAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAFtSURBVHicddJtV8IgFAdwD2zIgMEE1+NcqdsoK+m5tCyz7/+ZiLmHsyzvq53zO/cy -+N9ery1bVe9PWQA9z4MQ+H8Yoj7GASZ95IHfaBGmLOSchyIgyOu22mgQSjUcDuNYcoGjLiLK1cHh -0fHJaTKKOcMItgYxT89OzsfjyTTLC8UF0c2ZNmKquJhczq6ub+YmSVUYRF59GeDastu7+9nD41Nm -kiJ2jc2J3kAWZ9Pr55fH18XSmRuKUTXUaqHy7O19tfr4NFle/w3YDrWRUIlZrL/W86XJkyJVG9Ea -EjIx2XyZmZJGioeUaL+2AY8TY8omR6nkLKhu70zjUKVJXsp3quS2DVSJWNh3zzJKCyexI0ZxBP3a -fE0ElyqOlZJyw8r3BE2SFiJCyxA434SCkg65RhdeQBljQtCg39LWrA90RDDG1EWrYUO23hMANUKR -Rl61E529cR++D2G5LK002dr/qrcfu9u0V3bxn/XdhR/NYeeN0ggsLAAAACV0RVh0Q29tbWVudABj -bGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZzO7wAAAAASUVORK5CYII= ---=====================_715515186==_-- - diff --git a/sources/testing/samples/messages/m2009.txt b/sources/testing/samples/messages/m2009.txt deleted file mode 100644 index 7a79746..0000000 --- a/sources/testing/samples/messages/m2009.txt +++ /dev/null @@ -1,109 +0,0 @@ -Message-Id: <4.2.0.58.20000519003934.00a866f0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 2 (High) -Date: Fri, 19 May 2000 00:39:36 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/related; - type="text/html"; - boundary="=====================_715546120==_.REL" - ---=====================_715546120==_.REL -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - -3D"2aa658b6.png"
-
-Die Hasen und = -die -Fr=F6sche
-
-
Die Hasen klagten einst =FCber -ihre mi=DFliche Lage; "wir leben", sprach ein Redner, "in -steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, -ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der Tod selbst. -Auf, la=DFt uns ein f=FCr allemal sterben."
-
-In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt -erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie au= -fs -schnellste untertauchten.
-
-"Halt", rief nun eben dieser Sprecher, "wir wollen das -Ers=E4ufen noch ein wenig aufschieben, denn auch uns f=FCrchten, wie ihr -seht, einige Tiere, welche also wohl noch ungl=FCcklicher sein m=FCssen als -wir."
-
-3D"2aa658c0.png"
- ---=====================_715546120==_.REL -Content-Type: image/png; name="2aa658b6.png" -Content-ID: <4.2.0.58.20000519003934.00a866f0@pop.example.com.2> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa658b6.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_715546120==_.REL -Content-Type: image/png; name="2aa658c0.png" -Content-ID: <4.2.0.58.20000519003934.00a866f0@pop.example.com.3> -Content-Transfer-Encoding: base64 -Content-Disposition: inline; filename="2aa658c0.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAABAAALAAAVAAAa -AAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAjAAAWAAAmAABhAAB7AACGAACHAAB9AAB0 -AABgAAA5AAAUAAAGAAAnAABLAABvAACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABM -AAB3AACZAAC0GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHfhITm -f3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5PlrKzpmZntZWXvJSXXAADB -AACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADLICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2 -AAB4AABeAABAAAAiAABXAACSAADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABH -AAArAAAPAACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABPAAASAAAC -AABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADIAADTAADNAACzAACDAABuAAAe -AAB+AADAAACkAACNAAB/AABpAABQAAAwAACRAACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACs -AACvAACtAACmAACJAAB6AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABV -AACOAACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8AAA6AAAfAAAM -AAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAD8LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkFBDlQJf8zC/EIi4iK -iUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ -29ja2Ts4Ojkr6Li4urFDNf53N/Ow8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFW -SE1LF4A69n9GZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2YnOAj+ -d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1a/acUG5piNz/uXLzVJ2q -m6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2TVjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqV -tWXrtu07BJihcsw71+zanRW8Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZw -HBqL//8flz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/joOyYed5 -QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms1y9evXid7QZacgOxmSxktNzd -tSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAAJXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5 -IFl2ZXMgUGlndWV0NnM7vAAAAABJRU5ErkJggg== ---=====================_715546120==_.REL-- - diff --git a/sources/testing/samples/messages/m2010.txt b/sources/testing/samples/messages/m2010.txt deleted file mode 100644 index e33eebc..0000000 --- a/sources/testing/samples/messages/m2010.txt +++ /dev/null @@ -1,102 +0,0 @@ -Message-Id: <4.2.0.58.20000519004755.00a86100@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -Date: Fri, 19 May 2000 00:50:08 -0400 -To: Jürgen Schmürgen -From: Doug Sauder -Subject: The Hare and the Tortoise -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_716177458==_" - ---=====================_716177458==_ -Content-Type: text/plain; charset="us-ascii" - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, who replied, laughing: "Though you be swift as the wind, I will beat you in a race." The Hare, believing her assertion to be simply impossible, assented to the proposal; and they agreed that the Fox should choose the course and fix the goal. On the day appointed for the race the two started together. The Tortoise never for a moment stopped, but went on with a slow but steady pace straight to the end of the course. The Hare, lying down by the wayside, fell fast asleep. At last waking up, and moving as fast as he could, he saw the Tortoise had reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. - ---=====================_716177458==_ -Content-Type: image/png; name="greenball.png" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: attachment; filename="greenball.png" - - -begin 600 greenball.png -MB5!.1PT*&@H````-24A$4@```!L````;"`,```"Z"@1G```#`%!,5$7___\` -M````$```&```(0``"```,0``0@``4@``6@``2@`(8P`(P`0C``(:P`` -M.0``8P`0E``0G``AI0`0I0`AK0!"O1ACQC%CQCE2QB$IS@`8O0`0K0`8K0`A -MO0"4UF.MUGN$UE)*WA@AU@`8Q@`8M0``*0!"SA#.YY3>YZW&YY24YV-2YR$A -MW@`8S@!2WB&KRU;5>]/60`]SX,0^'\8HC[&`29]Y('?:!&F+.2< -MAR(@R.NVVF@02C4<#N-8#:LMN[^]G#XU-FDB)VC_PW8#K614(E9K+_6\Z7)DR)5 -M&]$:$C(QV7R9F9)&BH>4:+^V`8\38\HF1ZGD+*AN[TSC4*5)7LIWJN2V#52) -M6-AWSS)*"R>Q(T9Q!/W:?$T$ERJ.E9)RP\KW!$V2%B)"RQ`XWX2"D@ZY1A=> -M0!EC0M"@W]+6K`]T1##&U$6K84.VWA,`-4*11EZU$YV]<1^^#V&Y+*TTV=K_ -MJK>-T@@L+````"5T15AT0V]M;65N=`!C;&EP,F=I -F9B!V+C`N-B!B>2!9=F5S(%!I9W5E=#9S.[P`````245.1*Y"8((` -` -end - ---=====================_716177458==_ -Content-Type: image/png; name="blueball.png" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: attachment; filename="blueball.png" - - -begin 600 blueball.png -MB5!.1PT*&@H````-24A$4@```!L````;"`,```"Z"@1G```#`%!,5$7___\` -M``@``!```!@`````""D`$$(`$$H`"#$`$%(((7,(*7L(*80((6L`&%H`"#D( -M(6,0.9P80J480JT80K40.:40,90`&%(0,8PI4K4Y8\8Y8\XI6LX82LX80LX8 -M0KU":\9[G-:\ZMSMZESN]:C.\0,9Q:A-:MUN?.[^^,O>]*>^\8.:482L:, -MO>>UY^^4O>\``"E:C.=SI>]KG.\(*8PQ8^\I6N\((7L````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M```````````````````````````````````````````````````````````` -M``````````````````````````````````````````````````````````#! -M,@U5`````7123E,`0.;89@```!9T15AT4V]F='=AOF@```&(241!5'B<==)M5YLP&`;@$DZ`)A!2DX;,"4RG!2+:JHO;K&_; -M?/G_ORA[TE;LL<>;+YQSG3LAY!F-A@0H1"APHYT$.(HA48+#CXS'A*80DK$H -M0=OHT(*,54I*9+0#>J23(LO^U\/RJJ64PV(!L1,[WT[//I^?#)K6MGI -M+#)O>P8QG>P?GO:G9^?SIFP7BC"\*;J$:W%QV??]U8]Y4]7"4I:#>0PB,I'7 -M/_O^U^^;65/60H&%:PMCNKR]OKN_?_"U5B[>S3T66I0'?_[^N_&[U:+3A*_, -MC1QF3Z*=S9]?&D]R:M,L3@9+EW75@%1`"ZLIC_UW>GLLJ)5M65:W-=!2:<+\ -M^59F8J*F==U"28H.B,.2&PMSEJJE%%(N.JOT:U9$9K4D6("++%6VZZRU*O6$ -MUS5_B#`'U$HIG5+"BR@9"(HF'W-"*7TEG(^!UBNNS"&3QP7CG#.X=[-I;7XV -M8))'D!PF`FV3QP`9@XT)W^7M;OUK@.`9P&T-Q6ZVI^E3^8B[X[L#_P$45,S8 -MF6D.OP```"5T15AT0V]M;65N=`!C;&EP,F=I9B!V+C`N-B!B>2!9=F5S(%!I -49W5E=#9S.[P`````245.1*Y"8((` -` -end - ---=====================_716177458==_-- - diff --git a/sources/testing/samples/messages/m2011.txt b/sources/testing/samples/messages/m2011.txt deleted file mode 100644 index dacdc97..0000000 --- a/sources/testing/samples/messages/m2011.txt +++ /dev/null @@ -1,95 +0,0 @@ -Message-Id: <4.2.0.58.20000519005150.00a85230@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -Date: Fri, 19 May 2000 00:53:25 -0400 -To: =?iso-8859-1?Q?Jürgen?= =?iso-8859-1?Q?_Schmürgen?= - -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_716373961==_" - ---=====================_716373961==_ -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - ---=====================_716373961==_ -Content-Type: application/mac-binhex40; name="blueball.png" -Content-Disposition: attachment; filename="blueball.png" - - -(This file must be converted with BinHex 4.0) -:$'*XG@9LB@aX,R"ZC`"#58j"E@4[F`!!!!!&,3!!!!"'dSP36NF0#KS+!!!!$8P -)4&)!!!!E!!!!'`J$!!!!ZJS%C`!!!`"36&4&rrrr!!!)!!!3!!!B!!!!!!JT!"" -#!""+!!Ja!""5##&c##Pl##Q%##&V!"KD!!Jj##&M%$QF'%+P'%+Y'%+e%$QP%$' -8!"K5%$'-+9+e1@2'1@21+9V1'%V1'%,1'%+p3Q['Hjc@R,hHM,AHBj6R-@[H)9, -H'%V@'%,'%$'P!!!KcZI[jqr[R-E[8S6[+9VR%$QY5R[1VFlHTFl[@Sc[%$'F@S6 -@VGERcZr[M,h[5R[['$QP'%V'M,hRYHI[P,h[!!!T@ScRFkA[Djc[##Q--@2[+9V -[##&l!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`6)093!!!!&d8Nj6!%$Qf'B!!!! -@G%9BG&0[CR4hBA*P!'GTCM*`EQFJ-Li`,M%pAVjS!!!"L%P%394iR(A5E9HE-"J -'i"*1J#B38T1'c!P-T`8LfUU,fka[fhcjrlmSHp*@l,((QbqFFjdl)H3CMBB%+%3 -SF+1G"$L+)9'#`iq-ai5Q%*+a+%(Ek(*1*hY##M&9+5Q5d!hUNNb,,rYI$mUUPP- -0L!E%61pp1cckIR`bDeVCk5`bEhX'-ChX(jlfTfIRmkCX&iS`[#QkK'YaFGRhrG@ -2H929`P+@JhN-)M+4ecrlrYI[QeP6eN+"K@X,BlUm[Ekl[hr`Y9BZhXdp&PU8"hr -qrV[aZp@Ldi5[c)dFCNqLRFfIAaT2FQV6,%i'5jGeeB"83!ZV+BrpGhTl,+L9E9P -@YcA38QR#r2P@CQ+LTRAG3NQ+$SM$NKX,FjDUT445,MUVp'Y@4'De*&L!Lba9YZZ -XY5VeK0FeIiJ`"p4++Ce5`SXS'3L+*Kpc3LPp*Cb2JGBVVX`KNmF&ij`cZ(HcD@e -q0Q#54j!!(#B#EC2(!"Q$M3RIjHeZr@Z!i"R!E3h&EVDRk92jL,[MZ`2r!448c0L -CD3kr!!!!*A4&@(4$EfeYC@jd!'0XDA!bCfPQ)(BZ-#if)'*j)&PfCA-J8'PRG@9 -d0R-l[!!!!!"*48j%VN*JJYqd!!!!: - ---=====================_716373961==_ -Content-Type: application/mac-binhex40; name="HasenundFrösche.txt" -Content-Disposition: attachment; filename="HasenundFrösche.txt" - - -(This file must be converted with BinHex 4.0) -:%dKKFf9ZG@jN4R,fFf0SC5jdH(3!9%9B9(4dH(3!!!!!!Z-!!!!!4Pj%D@8J5'& -cC@iJG@jN)'4TC5"'F[CcBfKP$3e%D@8J5'&cC@iJDfaKCh4PEL"PD@jcG#$mBQ9 -b)'PSFQ8JE@RIE'PMD'8J6'&RC6XJ)RGTFL"XC@*PEL)X)(0`FQ&MD#"PD@iJ8Q9 -NEQ9b,#!LD@iJFh4PG'9b)%CeFQ0SG#"fEh)J6@9ZFf0SC@iJG@jN)&4TCA*PEL` -JC@PZC5"#CA9dC5"NCA)J5(9ZC'8X)'4PFL""C'aPFL`JDQ%JCQ&cG#"KE'aPFL" -5BA9LG'PPFQ8K)&9ZFf9bC5"cG'9dC5""EQGcG#"TFh3Jj(*RCA)JB@ac)'4PFL" -8Ef3JFf9XBR0d,L""G@BX)'aKhh3JG@jc)'9TEL"Qr()JB@aXC@eKE#"cG'9bBQ9 -Z,L)J$3e*EL"PD@jPE5"ZB@KPEL"8C@PMD#"hEfaXG'9Z)(0TC5"cD@0S)'jeEL" -PFR2NG@CPEMXJFfPP)'9TE(4PEL"TD'dJHR8l)'&XE'9TEL"NBA-JBAAICA*[FQ4 -PER4XD@0SC5"(CA6fFf8JG@jN)'PSFQ8JGh9ZC'9bBQ&bC5"(CA0dB@ad)'9bFf0 -SFQ9MDh4P)'9TEQ8J6@9ZCf8J4R,fFf0SC5`JC'PP)'&Y)&9QCA)JFf(IC@iX)(0 -[)(0PD()X)'4Khb"cD@8JBA9QFb"cBfKZC@aXFh4P)(9ZG'9bG'&eBfKdC@iZ)!d -0)NKKE(3L,#"bD@9Q)'jeEL"PBQ9Z)'4TCA0PFL"6F(*PBfKPFL`J)RGTFL"hEfa -XC@iJC'&c)%9bFq4eCQ9Z)'j[BfJJC@PZ)(GPEQPR)'&eCR0MD'PPBQ9Z,#"NC@j -Z)'&eBfJJG@jc)'EmFQ0SG'9Z,#"hD@8JD@Kb)(0PD(3X)'9TEQPRC5"8D@9bC5` -JGf9XBfKP)'&XFfmJGfpSE#"ZEf0S)(9ZCfcmBfYXD@0SCA)JFf9TEL"Yr(0cC@i -JB@ac)(GTFLiL)!d09'8!!!: - ---=====================_716373961==_-- - diff --git a/sources/testing/samples/messages/m2012.txt b/sources/testing/samples/messages/m2012.txt deleted file mode 100644 index 0d2abbd..0000000 --- a/sources/testing/samples/messages/m2012.txt +++ /dev/null @@ -1,108 +0,0 @@ -Message-Id: <4.2.0.58.20000519005505.00a853a0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -Date: Fri, 19 May 2000 00:56:13 -0400 -To: =?iso-8859-1?Q?Jürgen?= =?iso-8859-1?Q?_Schmürgen?= - -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_716541962==_" - ---=====================_716541962==_ -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - ---=====================_716541962==_ -Content-Type: image/png; name="blueball.png" -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8AAAgAABAAABgAAAAA -CCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkIIWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQ -MYwpUrU5Y8Y5Y84pWs4YSs4YQs4YQr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO -5+/n7++cxu9ShO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaMvee1 -5++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAADBMg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/bfPn/vyh70lbssceb -L5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEoQdvock4ne0IKMVUpKZLQDeqSTIsv+18P -yqqWUw2IBsRM7307PPp+fDJrWtnpLDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XC -UpaDeQwiMpHXP/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/MjRxm -T6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8+VZmYqKmdd1CSYoOiMOS -GwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B -1iuuzCGTxwXjnDO4d7NpbX42YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD -/wEUVMzYmWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBpZ3VldDZz -O7wAAAAASUVORK5CYII= ---=====================_716541962==_ -Content-Type: text/plain; charset="us-ascii" -Content-Disposition: attachment; filename="farmerandstork.txt" - - -The Farmer and the Stork - -A FARMER placed nets on his newly-sown plowlands and caught a -number of Cranes, which came to pick up his seed. With them he -trapped a Stork that had fractured his leg in the net and was -earnestly beseeching the Farmer to spare his life. "Pray save -me, Master," he said, "and let me go free this once. My broken -limb should excite your pity. Besides, I am no Crane, I am a -Stork, a bird of excellent character; and see how I love and -slave for my father and mother. Look too, at my feathers-- -they are not the least like those of a Crane." The Farmer -laughed aloud and said, "It may be all as you say, I only know -this: I have taken you with these robbers, the Cranes, and you -must die in their company." - -Birds of a feather flock together. - ---=====================_716541962==_ -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable -Content-Disposition: attachment; filename="HasenundFrösche.txt" - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - ---=====================_716541962==_-- - diff --git a/sources/testing/samples/messages/m2013.txt b/sources/testing/samples/messages/m2013.txt deleted file mode 100644 index 5cfc61c..0000000 --- a/sources/testing/samples/messages/m2013.txt +++ /dev/null @@ -1,84 +0,0 @@ -Message-Id: <4.2.0.58.20000519005818.00a86aa0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -Date: Fri, 19 May 2000 00:59:31 -0400 -To: Heinz =?iso-8859-1?Q?Müller?= -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: multipart/mixed; - boundary="=====================_716740438==_" - ---=====================_716740438==_ -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= - Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,= - der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= - Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;= - allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie= - aufs schnellste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= - wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,= - welche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - ---=====================_716740438==_ -Content-Type: text/plain; name="HasenundFrösche.txt"; - x-mac-type="54455854"; x-mac-creator="74747874" -Content-Transfer-Encoding: x-uuencode -Content-Disposition: attachment; filename="HasenundFrösche.txt" - - -begin 600 HasenundFrösche.txt -M1&EE($AA -X-Sender: dwsauder@pop.example.com (Unverified) -Disposition-Notification-To: -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 1 (Highest) -Date: Fri, 19 May 2000 01:06:18 -0400 -To: =?iso-8859-1?Q?Jürgen?= =?iso-8859-1?Q?_Schmürgen?= - -From: Doug Sauder -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Frösche?= -Mime-Version: 1.0 -Content-Type: text/plain; charset="iso-8859-1"; format=flowed -Content-Transfer-Encoding: quoted-printable - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ein= -=20 -Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde,=20 -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der= -=20 -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben." - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu;=20 -allein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt= - erschreckte=20 -eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie aufs= - schnellste=20 -untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein= -=20 -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere,=20 -welche also wohl noch ungl=FCcklicher sein m=FCssen als wir." - - diff --git a/sources/testing/samples/messages/m2015.txt b/sources/testing/samples/messages/m2015.txt deleted file mode 100644 index 7f06960..0000000 --- a/sources/testing/samples/messages/m2015.txt +++ /dev/null @@ -1,21 +0,0 @@ -Message-Id: <4.2.0.58.20000519010842.00a8c7f0@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 1 (Highest) -Date: Fri, 19 May 2000 01:10:27 -0400 -To: Heinz Müller -From: Doug Sauder -Subject: Die Hasen und die Frösche -Mime-Version: 1.0 -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: 8bit - -Die Hasen und die Frösche - -Die Hasen klagten einst über ihre mißliche Lage; "wir leben", sprach ein Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, der Adler, ja fast aller Raubtiere! Unsere stete Angst ist ärger als der Tod selbst. Auf, laßt uns ein für allemal sterben." - -In einem nahen Teich wollten sie sich nun ersäufen; sie eilten ihm zu; allein das außerordentliche Getöse und ihre wunderbare Gestalt erschreckte eine Menge Frösche, die am Ufer saßen, so sehr, daß sie aufs schnellste untertauchten. - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ersäufen noch ein wenig aufschieben, denn auch uns fürchten, wie ihr seht, einige Tiere, welche also wohl noch unglücklicher sein müssen als wir." - - diff --git a/sources/testing/samples/messages/m2016.txt b/sources/testing/samples/messages/m2016.txt deleted file mode 100644 index 43e73e7..0000000 --- a/sources/testing/samples/messages/m2016.txt +++ /dev/null @@ -1,18 +0,0 @@ -Message-Id: <4.2.0.58.20000519011207.00a8cd70@pop.example.com> -X-Sender: dwsauder@pop.example.com (Unverified) -X-Mailer: QUALCOMM Windows Eudora Pro Version 4.2.0.58 -X-Priority: 5 (Lowest) -Date: Fri, 19 May 2000 01:13:19 -0400 -To: Joe Blow -From: Doug Sauder -Subject: The Hare and the Tortoise -Mime-Version: 1.0 -Content-Type: text/plain; charset="us-ascii" - - -The Hare and the Tortoise - -A HARE one day ridiculed the short feet and slow pace of the Tortoise, who replied, laughing: "Though you be swift as the wind, I will beat you in a race." The Hare, believing her assertion to be simply impossible, assented to the proposal; and they agreed that the Fox should choose the course and fix the goal. On the day appointed for the race the two started together. The Tortoise never for a moment stopped, but went on with a slow but steady pace straight to the end of the course. The Hare, lying down by the wayside, fell fast asleep. At last waking up, and moving as fast as he could, he saw the Tortoise had reached the goal, and was comfortably dozing after her fatigue. - -Slow but steady wins the race. - diff --git a/sources/testing/samples/messages/m3001.txt b/sources/testing/samples/messages/m3001.txt deleted file mode 100644 index 30f74e5..0000000 --- a/sources/testing/samples/messages/m3001.txt +++ /dev/null @@ -1,96 +0,0 @@ -Date: Fri, 19 May 2000 09:55:48 -0400 (EDT) -From: Doug Sauder -To: Joe Blow -Subject: Test message from PINE -Message-ID: -MIME-Version: 1.0 -Content-Type: MULTIPART/MIXED; BOUNDARY="-1463757054-952513540-958744548=:8452" - - This message is in MIME format. The first part should be readable text, - while the remaining parts are likely unreadable without MIME-aware tools. - Send mail to mime@docserver.cac.washington.edu for more info. - ----1463757054-952513540-958744548=:8452 -Content-Type: TEXT/PLAIN; charset=US-ASCII - -This is a test message from PINE MUA. - - ----1463757054-952513540-958744548=:8452 -Content-Type: APPLICATION/octet-stream; name="redball.png" -Content-Transfer-Encoding: BASE64 -Content-ID: -Content-Description: A PNG graphic file -Content-Disposition: attachment; filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8A -AAABAAALAAAVAAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAj -AAAWAAAmAABhAAB7AACGAACHAAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABv -AACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABMAAB3AACZAAC0 -GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHf -hITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADL -ICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACS -AADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABHAAArAAAP -AACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABP -AAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADI -AADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6 -AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACO -AACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8 -AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkF -BDlQJf8zC/EIi4iKiUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp -6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja2Ts4Ojkr6Li4urFDNf53N/Ow -8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFWSE1LF4A69n9G -ZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1 -a/acUG5piNz/uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2T -VjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8 -Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZwHBqL//8f -lz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/ -joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAA -JXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7 -vAAAAABJRU5ErkJggg== ----1463757054-952513540-958744548=:8452 -Content-Type: APPLICATION/octet-stream; name="blueball.png" -Content-Transfer-Encoding: BASE64 -Content-ID: -Content-Description: A PNG graphic file -Content-Disposition: attachment; filename="blueball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8A -AAgAABAAABgAAAAACCkAEEIAEEoACDEAEFIIIXMIKXsIKYQIIWsAGFoACDkI -IWMQOZwYQqUYQq0YQrUQOaUQMZQAGFIQMYwpUrU5Y8Y5Y84pWs4YSs4YQs4Y -Qr1Ca8Z7nNacvd6Mtd5jlOcxa94hUt4YStYYQsYQMaUAACHO5+/n7++cxu9S -hO8pWucQOa1Ke86tzt6lzu9ajO8QMZxahNat1ufO7++Mve9Ke+8YOaUYSsaM -vee15++Uve8AAClajOdzpe9rnO8IKYwxY+8pWu8IIXsAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADB -Mg1VAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAGISURBVHicddJtV5swGAbgEk6AJhBSk4bMCUynBSLaqovbrG/b -fPn/vyh70lbsscebL5xznTsh5BmNhgQoRChwo50EOIohUYLDj4zHhKYQkrEo -Qdvock4ne0IKMVUpKZLQDeqSTIsv+18PyqqWUw2IBsRM7307PPp+fDJrWtnp -LDJvewYxnewfnvanZ+fzpmwXijC8KbqEa3Fx2ff91Y95U9XCUpaDeQwiMpHX -P/v+1++bWVPWQoGFawtjury9vru/f/C1Vi7ezT0WWpQHf/7+u/G71aLThK/M -jRxmT6KdzZ9fGk9yatMsTgZLl3XVgFRAC6spj/13enssqJVtWVa3NdBSacL8 -+VZmYqKmdd1CSYoOiMOSGwtzlqqlFFIuOqv0a1ZEZrUkWICLLFW266y1KvWE -1zV/iDAH1EopnVLCiygZCIomH3NCKX0lnI+B1iuuzCGTxwXjnDO4d7NpbX42 -YJJHkBwmAm2TxwAZg40J3+Xtbv1rgOAZwG0NxW62p+lT+Yi747sD/wEUVMzY -mWkOvwAAACV0RVh0Q29tbWVudABjbGlwMmdpZiB2LjAuNiBieSBZdmVzIFBp -Z3VldDZzO7wAAAAASUVORK5CYII= ----1463757054-952513540-958744548=:8452-- diff --git a/sources/testing/samples/messages/m3002.txt b/sources/testing/samples/messages/m3002.txt deleted file mode 100644 index e18c1ae..0000000 --- a/sources/testing/samples/messages/m3002.txt +++ /dev/null @@ -1,26 +0,0 @@ -Date: Fri, 19 May 2000 10:18:03 -0400 (EDT) -From: Doug Sauder -To: =?iso-8859-1?Q?J=FCrgen_Schm=FCrgen?= -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?= -Message-ID: -MIME-Version: 1.0 -Content-Type: TEXT/PLAIN; charset=iso-8859-1 -Content-Transfer-Encoding: QUOTED-PRINTABLE - -Die Hasen und die Fr=F6sche - -Die Hasen klagten einst =FCber ihre mi=DFliche Lage; "wir leben", sprach ei= -n Redner, "in steter Furcht vor Menschen und Tieren, eine Beute der Hunde, = -der Adler, ja fast aller Raubtiere! Unsere stete Angst ist =E4rger als der = -Tod selbst. Auf, la=DFt uns ein f=FCr allemal sterben."=20 - -In einem nahen Teich wollten sie sich nun ers=E4ufen; sie eilten ihm zu; al= -lein das au=DFerordentliche Get=F6se und ihre wunderbare Gestalt erschreckt= -e eine Menge Fr=F6sche, die am Ufer sa=DFen, so sehr, da=DF sie aufs schnel= -lste untertauchten.=20 - -"Halt", rief nun eben dieser Sprecher, "wir wollen das Ers=E4ufen noch ein = -wenig aufschieben, denn auch uns f=FCrchten, wie ihr seht, einige Tiere, we= -lche also wohl noch ungl=FCcklicher sein m=FCssen als wir."=20 - - diff --git a/sources/testing/samples/messages/m3003.txt b/sources/testing/samples/messages/m3003.txt deleted file mode 100644 index 640da6e..0000000 --- a/sources/testing/samples/messages/m3003.txt +++ /dev/null @@ -1,57 +0,0 @@ -Date: Fri, 19 May 2000 10:23:16 -0400 (EDT) -From: Doug Sauder -To: =?iso-8859-1?Q?Heinz_M=FCller?= -Subject: PNG graphic -Message-ID: -MIME-Version: 1.0 -Content-Type: MULTIPART/MIXED; BOUNDARY="-1463757054-170444605-958746196=:8452" - - This message is in MIME format. The first part should be readable text, - while the remaining parts are likely unreadable without MIME-aware tools. - Send mail to mime@docserver.cac.washington.edu for more info. - ----1463757054-170444605-958746196=:8452 -Content-Type: TEXT/PLAIN; charset=US-ASCII - - ----1463757054-170444605-958746196=:8452 -Content-Type: APPLICATION/octet-stream; name="redball.png" -Content-Transfer-Encoding: BASE64 -Content-ID: -Content-Description: red ball -Content-Disposition: attachment; filename="redball.png" - -iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAADAFBMVEX///8A -AAABAAALAAAVAAAaAAAXAAARAAAKAAADAAAcAAAyAABEAABNAABIAAA9AAAj -AAAWAAAmAABhAAB7AACGAACHAAB9AAB0AABgAAA5AAAUAAAGAAAnAABLAABv -AACQAAClAAC7AAC/AACrAAChAACMAABzAABbAAAuAAAIAABMAAB3AACZAAC0 -GRnKODjVPT3bKSndBQW4AACoAAB5AAAxAAAYAAAEAABFAACaAAC7JCTRYWHf -hITmf3/mVlbqHx/SAAC5AACjAABdAABCAAAoAAAJAABnAAC6Dw/QVFTek5Pl -rKzpmZntZWXvJSXXAADBAACxAACcAABtAABTAAA2AAAbAAAFAABKAACBAADL -ICDdZ2fonJzrpqbtiorvUVHvFBTRAADDAAC2AAB4AABeAABAAAAiAABXAACS -AADCAADaGxvoVVXseHjveHjvV1fvJibhAADOAAC3AACnAACVAABHAAArAAAP -AACdAADFAADhBQXrKCjvPDzvNTXvGxvjAADQAADJAAC1AACXAACEAABsAABP -AAASAAACAABiAADpAADvAgLnAADYAADLAAC6AACwAABwAAATAAAkAABYAADI -AADTAADNAACzAACDAABuAAAeAAB+AADAAACkAACNAAB/AABpAABQAAAwAACR -AACpAAC8AACqAACbAABlAABJAAAqAAAOAAA0AACsAACvAACtAACmAACJAAB6 -AABrAABaAAA+AAApAABqAACCAACfAACeAACWAACPAAB8AAAZAAAHAABVAACO -AACKAAA4AAAQAAA/AAByAACAAABcAAA3AAAsAABmAABDAABWAAAgAAAzAAA8 -AAA6AAAfAAAMAAAdAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8 -LtlFAAAAAXRSTlMAQObYZgAAABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjAu -MT1evmgAAAIISURBVHicY2CAg/8QwIABmJhZWFnZ2Dk4MaU5uLh5eHn5+LkF -BDlQJf8zC/EIi4iKiUtI8koJScsgyf5nlpWTV1BUUlZRVVPX4NFk1UJIyghp -6+jq6RsYGhmbKJgK85mZW8Dk/rNaSlhZ29ja2Ts4Ojkr6Li4urFDNf53N/Ow -8vTy9vH18w8IDAoWDQkNC4+ASP5ni4wKio6JjYtPSExKTnFWSE1LF4A69n9G -ZlZ2Tm5efkFhUXFySWlZlEd5RSVY7j+TkGRVdU1tXX1DY1Ozcktpa1t7h2Yn -OAj+d7l1tyo79vT29SdNSJ44SbFVdHIo9xSIHNPUaWqTpifNSJrZnK00S0U1 -a/acUG5piNz/uXLzVJ2qm6dXz584S2WB1cJFi5cshZr539xVftnyFKUVTi2T -VjqvyhJLXb1m7TqoHPt6F/HW0g0bN63crGqVtWXrtu07BJihcsw71+zanRW8 -Z89eq337RQ/Ip60xO3gIElX/LbikDm8T36KwbNmRo7O3zpHkPSZwHBqL//8f -lz1x2OOkyKJTi7aqbzutfUZI2gIuF8F2lr/D5dw2+fZdwpl8YVOlI+CJ4/9/ -joOyYed5QzMvhGqnm2V0WiClm///D0lfXHtJ6vLlK9w7rx7vQk5SQJbFtSms -1y9evXid7QZacgOxmSxktNzdtSwwU+J/VICaCPFIYU3XAJhIOtjf5sfyAAAA -JXRFWHRDb21tZW50AGNsaXAyZ2lmIHYuMC42IGJ5IFl2ZXMgUGlndWV0NnM7 -vAAAAABJRU5ErkJggg== ----1463757054-170444605-958746196=:8452-- diff --git a/sources/testing/samples/messages/m3004.txt b/sources/testing/samples/messages/m3004.txt deleted file mode 100644 index 99d79e4..0000000 --- a/sources/testing/samples/messages/m3004.txt +++ /dev/null @@ -1,42 +0,0 @@ -Date: Fri, 19 May 2000 10:26:12 -0400 (EDT) -From: Doug Sauder -To: Joe Blow , - =?iso-8859-1?Q?Heinz_M=FCller?= -Subject: =?iso-8859-1?Q?Die_Hasen_und_die_Fr=F6sche?= -Message-ID: -MIME-Version: 1.0 -Content-Type: MULTIPART/MIXED; BOUNDARY="-1463757054-891160829-958746372=:8452" - - This message is in MIME format. The first part should be readable text, - while the remaining parts are likely unreadable without MIME-aware tools. - Send mail to mime@docserver.cac.washington.edu for more info. - ----1463757054-891160829-958746372=:8452 -Content-Type: TEXT/PLAIN; charset=US-ASCII - - ----1463757054-891160829-958746372=:8452 -Content-Type: TEXT/PLAIN; charset=iso-8859-1; name*="iso-8859-1''HasenundFr%F6sche.txt" -Content-Transfer-Encoding: BASE64 -Content-ID: -Content-Description: Short story in German -Content-Disposition: attachment; filename*="iso-8859-1''HasenundFr%F6sche.txt" - -RGllIEhhc2VuIHVuZCBkaWUgRnL2c2NoZQ0NCg0NCkRpZSBIYXNlbiBrbGFn -dGVuIGVpbnN0IPxiZXIgaWhyZSBtad9saWNoZSBMYWdlOyAid2lyIGxlYmVu -Iiwgc3ByYWNoIGVpbiBSZWRuZXIsICJpbiBzdGV0ZXIgRnVyY2h0IHZvciBN -ZW5zY2hlbiB1bmQgVGllcmVuLCBlaW5lIEJldXRlIGRlciBIdW5kZSwgZGVy -IEFkbGVyLCBqYSBmYXN0IGFsbGVyIFJhdWJ0aWVyZSEgVW5zZXJlIHN0ZXRl -IEFuZ3N0IGlzdCDkcmdlciBhbHMgZGVyIFRvZCBzZWxic3QuIEF1ZiwgbGHf -dCB1bnMgZWluIGb8ciBhbGxlbWFsIHN0ZXJiZW4uIiANDQoNDQpJbiBlaW5l -bSBuYWhlbiBUZWljaCB3b2xsdGVuIHNpZSBzaWNoIG51biBlcnPkdWZlbjsg -c2llIGVpbHRlbiBpaG0genU7IGFsbGVpbiBkYXMgYXXfZXJvcmRlbnRsaWNo -ZSBHZXT2c2UgdW5kIGlocmUgd3VuZGVyYmFyZSBHZXN0YWx0IGVyc2NocmVj -a3RlIGVpbmUgTWVuZ2UgRnL2c2NoZSwgZGllIGFtIFVmZXIgc2HfZW4sIHNv -IHNlaHIsIGRh3yBzaWUgYXVmcyBzY2huZWxsc3RlIHVudGVydGF1Y2h0ZW4u -IA0NCg0NCiJIYWx0IiwgcmllZiBudW4gZWJlbiBkaWVzZXIgU3ByZWNoZXIs -ICJ3aXIgd29sbGVuIGRhcyBFcnPkdWZlbiBub2NoIGVpbiB3ZW5pZyBhdWZz -Y2hpZWJlbiwgZGVubiBhdWNoIHVucyBm/HJjaHRlbiwgd2llIGlociBzZWh0 -LCBlaW5pZ2UgVGllcmUsIHdlbGNoZSBhbHNvIHdvaGwgbm9jaCB1bmds/GNr -bGljaGVyIHNlaW4gbfxzc2VuIGFscyB3aXIuIiANDQoNDQo= ----1463757054-891160829-958746372=:8452-- diff --git a/sources/testing/samples/messages/m4000.txt b/sources/testing/samples/messages/m4000.txt deleted file mode 100644 index 25bb317..0000000 --- a/sources/testing/samples/messages/m4000.txt +++ /dev/null @@ -1,629 +0,0 @@ -Return-Path: -X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on xxxxxxx -X-Spam-Level: -X-Spam-Status: No, score=0.1 required=5.0 tests=ALL_TRUSTED,HTML_MESSAGE, - MIME_HTML_ONLY autolearn=no autolearn_force=no version=3.4.0 -Delivered-To: xxxxx -Received: from localhost (xxxxxxxx [127.0.0.1]) - by xxxxxxxx (Postfix) with ESMTP id 128C9500AAB - for ; Thu, 24 Jul 2014 14:39:01 +0200 (CEST) -X-Virus-Scanned: Debian amavisd-new at xxxxxxxxxx -Received: from xxxxxxx ([127.0.0.1]) - by localhost (xxxxxxxxxxxxx [127.0.0.1]) (amavisd-new, port 10024) - with ESMTP id Q0uBkN509meP for ; - Thu, 24 Jul 2014 14:39:00 +0200 (CEST) -Received: from xxxxxxxxx (xxxxxxxxxx [192.168.1.21]) - (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) - (Client CN "xxxxxxxxxxxxx", Issuer "Go Daddy Secure Certificate Authority - G2" (verified OK)) - by xxxxxxxxxxxxx (Postfix) with ESMTPS id B5372500AA0 - for ; Thu, 24 Jul 2014 14:39:00 +0200 (CEST) -Received: from xxxxxxxxxxx ([192.168.1.21]) by post ([192.168.1.21]) - with mapi id 14.03.0195.001; Thu, 24 Jul 2014 14:39:03 +0200 -From: xxxxxxxxxxx -To: xxxxxxxx -Subject: Fwd: test -Thread-Topic: test -Thread-Index: Ac+nKMy+QShsqrH0RSe5w7IViN06kAAE3Mrp -Date: Thu, 24 Jul 2014 12:39:03 +0000 -Message-ID: <620D4BDC-9F59-4476-A07C-1AC7A065375E@xxxxxxxxxx> -References: <35DB5704EA64874BACB0731D6103030C4EB26D@post> -In-Reply-To: <35DB5704EA64874BACB0731D6103030C4EB26D@post> -Accept-Language: nb-NO, en-US -Content-Language: nb-NO -X-MS-Has-Attach: -X-MS-TNEF-Correlator: -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable -MIME-Version: 1.0 - - - - - - -


-
-Sendt fra min iPhone
-

-Videresendt melding:
-
-
-
-
Fra: xxxxxxxxxxxx <xxxxxxx@xxxxxxxxxx>
-Dato: 24. juli 2014 12:19:49 CEST
-Til: xxxxxxxxxx <xxxx.= -xxxxxxxxxxxxxx>
-Emne: test
-
-
-
-
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

xxxxxxxxxxx<= -/span>

-
-

Tlf dagsenter: xxxxxxxx= -

-
-

Rute 5243

-
-

L=F8yve

-
-

L=F8yve

-
-
-

Dere m=E5 gi beskjed til dag= -senter hvis noen ikke vil bli med i dag

-
-
-

Fredag

-
-

Uke 30

-
-

Mrk -

-

08:15

-
-

14:30

-
-

Telefon

-
-

Vi setter av ved dags= -enteret mellom klokken 08:40 og 09:00

-
- - -
-

Kj=F8reliste for S=F8rsia/= - Krossen

-
- - - -
-

xxxxxxx, leil 304

-
-

xxxxxxxxxxxxxx

-
-

rlt

-
-

T-2267

-
-

T-2267

-
-

xxx -54139

-
-

xxxxxxxx 16

-
-

xxxxxxxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

xxxxx= -54410

-
-

xxxxxxxxxxx

-
-

xxxxxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

xxxxxxxx

-
-

xxxxxxxxx

-
-

xxxxxxx

-
-

rlt

-
-

T-2267

-
-

T-2267

-
-

xxxxx -54220

-
-

xxxxxxx

-
-

xxxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

5xxxxxx7

-
-

xxxxxxxx

-
-

xxxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

xxxx -50392

-
-

xxxxxxx, inng. A

-
-

xxxxxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

xxxx= -96170

-
-

xxxxxxxx. B

-
-

xxxxxxxx

-
-

rlt. ring 5min f= -=F8r

-
-

T-2267

-
-

T-2267

-
-

xxxxx -50976

-
-

R=E5dxxxxxx, leil 401

-
-

xxxxxxx

-
- -

T-2267

-
-

T-2267

-
-

xxxx -57776

-
-

R=E5dhxxxxxxx28, leil 302

-
-

xxxxxxx

-
-

rlt

-
-

T-2267

-
-

T-2267

-
-

xxxx -50894

-
-

xxxxxxxxx m=E5 f=F8lges helt inn. Leilighetsn= -r 304.

-
- - -
-

xxxxxxxxringes det til 5 min f=F8r

-
- - - -
-

Husk at vi kan xxxxx.= -

-
- - - -
-

 

-
-
-
- - diff --git a/sources/testing/samples/messages/smime001.txt b/sources/testing/samples/messages/smime001.txt deleted file mode 100644 index 6f51aa3..0000000 --- a/sources/testing/samples/messages/smime001.txt +++ /dev/null @@ -1,40 +0,0 @@ -MIME-Version: 1.0 -To: User2@examples.com -From: aliceDss@examples.com -Subject: Example 4.8 -Message-Id: <020906002550300.249@examples.com> -Date: Fri, 06 Sep 2002 00:25:21 -0300 -Content-Type: multipart/signed; - micalg=SHA1; - boundary="----=_NextBoundry____Fri,_06_Sep_2002_00:25:21"; - protocol="application/pkcs7-signature" - -This is a multi-part message in MIME format. - -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21 - -This is some sample content. -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21 -Content-Type: application/pkcs7-signature; name=smime.p7s -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename=smime.p7s - -MIIDdwYJKoZIhvcNAQcCoIIDaDCCA2QCAQExCTAHBgUrDgMCGjALBgkqhkiG9w0BBwGgggL -gMIIC3DCCApugAwIBAgICAMgwCQYHKoZIzjgEAzASMRAwDgYDVQQDEwdDYXJsRFNTMB4XDT -k5MDgxNzAxMTA0OVoXDTM5MTIzMTIzNTk1OVowEzERMA8GA1UEAxMIQWxpY2VEU1MwggG2M -IIBKwYHKoZIzjgEATCCAR4CgYEAgY3N7YPqCp45PsJIKKPkR5PdDteoDuxTxauECE//lOFz -SH4M1vNESNH+n6+koYkv4dkwyDbeP5u/t0zcX2mK5HXQNwyRCJWb3qde+fz0ny/dQ6iLVPE -/sAcIR01diMPDtbPjVQh11Tl2EMR4vf+dsISXN/LkURu15AmWXPN+W9sCFQDiR6YaRWa4E8 -baj7g3IStii/eTzQKBgCY40BSJMqo5+z5t2UtZakx2IzkEAjVc8ssaMMMeUF3dm1nizaoFP -VjAe6I2uG4Hr32KQiWn9HXPSgheSz6Q+G3qnMkhijt2FOnOLl2jB80jhbgvMAF8bUmJEYk2 -RL34yJVKU1a14vlz7BphNh8Rf8K97dFQ/5h0wtGBSmA5ujY5A4GEAAKBgFzjuVp1FJYLqXr -d4z+p7Kxe3L23ExE0phaJKBEj2TSGZ3V1ExI9Q1tv5VG/+onyohs+JH09B41bY8i7RaWgSu -OF1s4GgD/oI34a8iSrUxq4Jw0e7wi/ZhSAXGKsZfoVi/G7NNTSljf2YUeyxDKE8H5BQP1Gp -2NOM/Kl4vTyg+W4o4GBMH8wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBsAwHwYDVR0j -BBgwFoAUcEQ+gi5vh95K03XjPSC8QyuT8R8wHQYDVR0OBBYEFL5sobPjwfftQ3CkzhMB4v3 -jl/7NMB8GA1UdEQQYMBaBFEFsaWNlRFNTQGV4YW1wbGUuY29tMAkGByqGSM44BAMDMAAwLQ -IUVQykGR9CK4lxIjONg2q1PWdrv0UCFQCfYVNSVAtcst3a53Yd4hBSW0NevTFjMGECAQEwG -DASMRAwDgYDVQQDEwdDYXJsRFNTAgIAyDAHBgUrDgMCGjAJBgcqhkjOOAQDBC4wLAIUM/mG -f6gkgp9Z0XtRdGimJeB/BxUCFGFFJqwYRt1WYcIOQoGiaowqGzVI - -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21-- diff --git a/sources/testing/samples/messages/smime002.txt b/sources/testing/samples/messages/smime002.txt deleted file mode 100644 index 7934c48..0000000 --- a/sources/testing/samples/messages/smime002.txt +++ /dev/null @@ -1,18 +0,0 @@ -MIME-Version: 1.0 -Message-Id: <00103112005203.00349@amyemily.ig.com> -Date: Tue, 31 Oct 2000 12:00:52 -0600 (Central Standard Time) -From: User1 -To: User2 -Subject: Example 5.3 -Content-Type: application/pkcs7-mime; - name=smime.p7m; - smime-type=enveloped-data -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename=smime.p7m - -MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYDVQQDEwdDYXJ -sUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUABIGAC3EN5nGIiJi2lsGPcP -2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FBs3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadC -DgO8/nUkUNYeNxJtuzubGgzoyEd8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHR -LFf02hosdR8wQwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43 -LrY4OxUk660cu1lXeCSFOSOpOJ7FuVyU= diff --git a/sources/testing/samples/messages/zpush-html-preview-bug.txt b/sources/testing/samples/messages/zpush-html-preview-bug.txt deleted file mode 100644 index ad3e4e9..0000000 --- a/sources/testing/samples/messages/zpush-html-preview-bug.txt +++ /dev/null @@ -1,66 +0,0 @@ -Envelope-to: TOUSER@DOMAIN.COM -Delivery-date: Wed, 04 Nov 2015 18:55:14 -0500 -From: "FROMUSER" -To: "TOUSER" -Subject: Jane's back -Date: Wed, 4 Nov 2015 17:54:35 -0600 -MIME-Version: 1.0 -Content-Type: multipart/alternative; - boundary="----=_NextPart_000_05F0_01D11729.DDC49140" -X-Priority: 3 -X-MSMail-Priority: Normal -Importance: Normal -X-Mailer: Microsoft Windows Live Mail 15.4.3508.1109 -X-MimeOLE: Produced By Microsoft MimeOLE V15.4.3508.1109 - -This is a multi-part message in MIME format. - -------=_NextPart_000_05F0_01D11729.DDC49140 -Content-Type: text/plain; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - -How is she doing? - -Dad - ---- -This email has been checked for viruses by Avast antivirus software. -https://www.avast.com/antivirus - -------=_NextPart_000_05F0_01D11729.DDC49140 -Content-Type: text/html; - charset="iso-8859-1" -Content-Transfer-Encoding: quoted-printable - - - -
-
-
How is she doing?
-
 
-
Dad
-

-
- - - - - -
- - 3D"Avast - - -

- This email has been checked for viruses by Avast antivirus software. -
www.avast.com -

-
-
- - -------=_NextPart_000_05F0_01D11729.DDC49140-- diff --git a/sources/testing/samples/smime.txt b/sources/testing/samples/smime.txt deleted file mode 100644 index 7c846c7..0000000 --- a/sources/testing/samples/smime.txt +++ /dev/null @@ -1,63 +0,0 @@ -https://tools.ietf.org/html/rfc4134 - -MIME-Version: 1.0 -To: User2@examples.com -From: aliceDss@examples.com -Subject: Example 4.8 -Message-Id: <020906002550300.249@examples.com> -Date: Fri, 06 Sep 2002 00:25:21 -0300 -Content-Type: multipart/signed; - micalg=SHA1; - boundary="----=_NextBoundry____Fri,_06_Sep_2002_00:25:21"; - protocol="application/pkcs7-signature" - -This is a multi-part message in MIME format. - -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21 - -This is some sample content. -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21 -Content-Type: application/pkcs7-signature; name=smime.p7s -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename=smime.p7s - -MIIDdwYJKoZIhvcNAQcCoIIDaDCCA2QCAQExCTAHBgUrDgMCGjALBgkqhkiG9w0BBwGgggL -gMIIC3DCCApugAwIBAgICAMgwCQYHKoZIzjgEAzASMRAwDgYDVQQDEwdDYXJsRFNTMB4XDT -k5MDgxNzAxMTA0OVoXDTM5MTIzMTIzNTk1OVowEzERMA8GA1UEAxMIQWxpY2VEU1MwggG2M -IIBKwYHKoZIzjgEATCCAR4CgYEAgY3N7YPqCp45PsJIKKPkR5PdDteoDuxTxauECE//lOFz -SH4M1vNESNH+n6+koYkv4dkwyDbeP5u/t0zcX2mK5HXQNwyRCJWb3qde+fz0ny/dQ6iLVPE -/sAcIR01diMPDtbPjVQh11Tl2EMR4vf+dsISXN/LkURu15AmWXPN+W9sCFQDiR6YaRWa4E8 -baj7g3IStii/eTzQKBgCY40BSJMqo5+z5t2UtZakx2IzkEAjVc8ssaMMMeUF3dm1nizaoFP -VjAe6I2uG4Hr32KQiWn9HXPSgheSz6Q+G3qnMkhijt2FOnOLl2jB80jhbgvMAF8bUmJEYk2 -RL34yJVKU1a14vlz7BphNh8Rf8K97dFQ/5h0wtGBSmA5ujY5A4GEAAKBgFzjuVp1FJYLqXr -d4z+p7Kxe3L23ExE0phaJKBEj2TSGZ3V1ExI9Q1tv5VG/+onyohs+JH09B41bY8i7RaWgSu -OF1s4GgD/oI34a8iSrUxq4Jw0e7wi/ZhSAXGKsZfoVi/G7NNTSljf2YUeyxDKE8H5BQP1Gp -2NOM/Kl4vTyg+W4o4GBMH8wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBsAwHwYDVR0j -BBgwFoAUcEQ+gi5vh95K03XjPSC8QyuT8R8wHQYDVR0OBBYEFL5sobPjwfftQ3CkzhMB4v3 -jl/7NMB8GA1UdEQQYMBaBFEFsaWNlRFNTQGV4YW1wbGUuY29tMAkGByqGSM44BAMDMAAwLQ -IUVQykGR9CK4lxIjONg2q1PWdrv0UCFQCfYVNSVAtcst3a53Yd4hBSW0NevTFjMGECAQEwG -DASMRAwDgYDVQQDEwdDYXJsRFNTAgIAyDAHBgUrDgMCGjAJBgcqhkjOOAQDBC4wLAIUM/mG -f6gkgp9Z0XtRdGimJeB/BxUCFGFFJqwYRt1WYcIOQoGiaowqGzVI - -------=_NextBoundry____Fri,_06_Sep_2002_00:25:21-- - - - -MIME-Version: 1.0 -Message-Id: <00103112005203.00349@amyemily.ig.com> -Date: Tue, 31 Oct 2000 12:00:52 -0600 (Central Standard Time) -From: User1 -To: User2 -Subject: Example 5.3 -Content-Type: application/pkcs7-mime; - name=smime.p7m; - smime-type=enveloped-data -Content-Transfer-Encoding: base64 -Content-Disposition: attachment; filename=smime.p7m - -MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYDVQQDEwdDYXJ -sUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUABIGAC3EN5nGIiJi2lsGPcP -2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FBs3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadC -DgO8/nUkUNYeNxJtuzubGgzoyEd8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHR -LFf02hosdR8wQwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43 -LrY4OxUk660cu1lXeCSFOSOpOJ7FuVyU= diff --git a/sources/testing/testing-bug68532fixed.php b/sources/testing/testing-bug68532fixed.php deleted file mode 100644 index d0acf71..0000000 --- a/sources/testing/testing-bug68532fixed.php +++ /dev/null @@ -1,22 +0,0 @@ -CheckConnection()); - -// Show options supported by server -$options = $caldav->DoOptionsRequest(); -print_r($options); - -$calendars = $caldav->FindCalendars(); -print_r($calendars); - -$path = $caldav_path . CALDAV_PERSONAL . "/"; -$val = $caldav->GetCalendarDetails($path); -print_r($val); - -$begin = gmdate("Ymd\THis\Z", time() - 24*7*60*60); -$finish = gmdate("Ymd\THis\Z", CALDAV_MAX_SYNC_PERIOD); -$msgs = $caldav->GetEvents($begin, $finish, $path); -print_r($msgs); - -// Initial sync -$results = $caldav->GetSync($path, true, CALDAV_SUPPORTS_SYNC); -print_r($results); - -sleep(60); - -$results = $caldav->GetSync($path, false, CALDAV_SUPPORTS_SYNC); -print_r($results); diff --git a/sources/testing/testing-caldav_search.php b/sources/testing/testing-caldav_search.php deleted file mode 100644 index 2a0b0b0..0000000 --- a/sources/testing/testing-caldav_search.php +++ /dev/null @@ -1,39 +0,0 @@ -CheckConnection()); - -$path = $caldav_path . CALDAV_PERSONAL . "/"; - -$filter =<< - - - - 040000008200E00074C5B7101A82E00800000000B72BEB1EF3CCD00100000000000000001000000067299FC1A8990B4EB88510710DB15426 - - - - -EOFFILTER; -$val = $caldav->DoCalendarQuery($filter, $path); -print_r($val); diff --git a/sources/testing/testing-carddav.php b/sources/testing/testing-carddav.php deleted file mode 100644 index 0840961..0000000 --- a/sources/testing/testing-carddav.php +++ /dev/null @@ -1,63 +0,0 @@ -set_auth($username, $password); -//$server->enable_debug(); -$raw = $server->get(false, false, true); -echo "$raw\n"; -//var_dump($server->get_debug()); - -if ($raw !== false) { - $xml = new SimpleXMLElement($raw); - foreach($xml->addressbook_element as $response) { - if ($gal_url !== false) { - if (strcmp(urldecode($response->url), $gal_url) == 0) { - echo sprintf("BackendCardDAV::discoverAddressbooks() Ignoring GAL addressbook '%s'\n", $this->gal_url); - continue; - } - } - - echo sprintf("BackendCardDAV::discoverAddressbooks() Found addressbook '%s'\n", urldecode($response->url)); - } - unset($xml); -} - -//$server->enable_debug(); -$server->set_url($default_url); -$vcards = $server->do_sync(true, false, false); -//var_dump($server->get_debug()); -echo "$vcards\n"; - -echo "-----------\n"; -//$server->enable_debug(); -// TODO: set to an existing vcard ID (you will get a list with the do_sync operation -$xml = $server->get_xml_vcard('131-52C19B00-7-7A512880'); -//var_dump($server->get_debug()); -echo "$xml\n"; \ No newline at end of file diff --git a/sources/testing/testing-forward.php b/sources/testing/testing-forward.php deleted file mode 100644 index ac645cb..0000000 --- a/sources/testing/testing-forward.php +++ /dev/null @@ -1,218 +0,0 @@ -decode(array('decode_headers' => false, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); -unset($mobj); - -$finalEmail = new Mail_mimePart('', array('content_type' => 'multipart/mixed')); -printf("%s\n", sprintf("BackendIMAP->SendMail(): is a new message or we are replacing mime")); -addTextPartsMessage($finalEmail, $message); -if (isset($message->parts)) { - printf("%s\n", sprintf("BackendIMAP->SendMail(): we have extra parts")); - // We add extra parts from the new message - addExtraSubParts($finalEmail, $message->parts); -} - -// We encode the final message -$boundary = '=_' . md5(rand() . microtime()); -$finalEmail = $finalEmail->encode($boundary); - -$finalHeaders = array('Mime-Version' => '1.0'); -// We copy all the headers, minus content_type -printf("%s\n", sprintf("BackendIMAP->SendMail(): Copying new headers")); -foreach ($message->headers as $k => $v) { - if (strcasecmp($k, 'content-type') != 0 && strcasecmp($k, 'content-transfer-encoding') != 0 && strcasecmp($k, 'mime-version') != 0) { - $finalHeaders[ucwords($k)] = $v; - } -} -foreach ($finalEmail['headers'] as $k => $v) { - $finalHeaders[$k] = $v; -} - -$finalBody = "This is a multi-part message in MIME format.\n" . $finalEmail['body']; - -unset($sourceMail); -unset($message); -unset($sourceMessage); -unset($finalEmail); - -printf("%s\n", sprintf("BackendIMAP->SendMail(): Final mail to send:")); -foreach ($finalHeaders as $k => $v) - printf("%s\n", sprintf("%s: %s", $k, $v)); -printf("\n"); -foreach (preg_split("/((\r)?\n)/", $finalBody) as $bodyline) - printf("%s\n", sprintf("%s", $bodyline)); - - - - /** - * Add text parts to a mimepart object - * - * @param Mail_mimePart $email reference to the object - * @param Mail_mimeDecode $message reference to the message - * - * @access private - * @return void - */ - function addTextPartsMessage(&$email, &$message) { - $htmlBody = $plainBody = ''; - getBodyRecursive($message, "html", $htmlBody); - getBodyRecursive($message, "plain", $plainBody); - - $altEmail = new Mail_mimePart('', array('content_type' => 'multipart/alternative')); - - if (strlen($htmlBody) > 0) { - printf("%s\n", sprintf("BackendIMAP->addTextPartsMessage(): The message has HTML body")); - $altEmail->addSubPart($htmlBody, array('content_type' => 'text/html; charset=utf-8', 'encoding' => 'base64')); - } - if (strlen($plainBody) > 0) { - printf("%s\n", sprintf("BackendIMAP->addTextPartsMessage(): The message has PLAIN body")); - $altEmail->addSubPart($plainBody, array('content_type' => 'text/plain; charset=utf-8', 'encoding' => 'base64')); - } - - $boundary = '=_' . md5(rand() . microtime()); - $altEmail = $altEmail->encode($boundary); - - $email->addSubPart($altEmail['body'], array('content_type' => 'multipart/alternative;'."\n".' boundary="'.$boundary.'"')); - - unset($altEmail); - - unset($htmlBody); - unset($plainBody); - } - - /** - * Get all parts in the message with specified type and concatenate them together, unless the - * Content-Disposition is 'attachment', in which case the text is apparently an attachment - * - * @param string $message mimedecode message(part) - * @param string $message message subtype - * @param string &$body body reference - * - * @access protected - * @return - */ - function getBodyRecursive($message, $subtype, &$body) { - if(!isset($message->ctype_primary)) return; - if(strcasecmp($message->ctype_primary,"text")==0 && strcasecmp($message->ctype_secondary,$subtype)==0 && isset($message->body)) - $body .= $message->body; - - if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) { - foreach($message->parts as $part) { - if(!isset($part->disposition) || strcasecmp($part->disposition,"attachment")) { - getBodyRecursive($part, $subtype, $body); - } - } - } - } - - /** - * Add extra parts (not text; inlined or attached parts) to a mimepart object. - * - * @param Mail_mimePart $email reference to the object - * @param array $parts array of parts - * - * @access private - * @return void - */ - function addExtraSubParts(&$email, $parts) { - if (isset($parts)) { - foreach ($parts as $part) { - $new_part = null; - // Only if it's an attachment we will add the text parts, because all the inline/no disposition have been already added - if (isset($part->disposition) && $part->disposition == "attachment") { - printf("%s\n", sprintf("BackendIMAP->addExtraSubParts(): extraSubPart attachment found")); - // it's an attachment - $new_part = addSubPart($email, $part); - } - else { - printf("%s\n", sprintf("BackendIMAP->addExtraSubParts(): extraSubPart no attachment found")); - if (isset($part->ctype_primary) && $part->ctype_primary != "text" && $part->ctype_primary != "multipart") { - printf("%s\n", sprintf("BackendIMAP->addExtraSubParts(): it's not a text part or a multipart")); - // it's not a text part or a multipart - $new_part = addSubPart($email, $part); - } - } - if (isset($part->parts)) { - printf("%s\n", sprintf("BackendIMAP->addExtraSubParts(): found subparts to my sub-part. Recursive calling")); - // We add sub-parts to the new part, not to the main message - addExtraSubParts($new_part === null ? $email : $new_part, $part->parts); - } - } - } - } - - /** - * Add a subpart to a mimepart object. - * - * @param Mail_mimePart $email reference to the object - * @param object $part message part - * - * @access private - * @return void - */ - function addSubPart(&$email, $part) { - //http://tools.ietf.org/html/rfc4021 - $new_part = null; - $params = array(); - if (isset($part) && isset($email)) { - if (isset($part->ctype_primary)) { - $params['content_type'] = $part->ctype_primary; - } - if (isset($part->ctype_secondary)) { - $params['content_type'] .= '/' . $part->ctype_secondary; - } - if (isset($part->ctype_parameters)) { - foreach ($part->ctype_parameters as $k => $v) { - if(strcasecmp($k, 'boundary') != 0) { - $params['content_type'] .= '; ' . $k . '=' . $v; - } - } - } - if (isset($part->disposition)) { - $params['disposition'] = $part->disposition; - } - //FIXME: dfilename => filename - if (isset($part->d_parameters)) { - foreach ($part->d_parameters as $k => $v) { - $params[$k] = $v; - } - } - foreach ($part->headers as $k => $v) { - switch($k) { - case "content-description": - $params['description'] = $v; - break; - case "content-type": - case "content-disposition": - case "content-transfer-encoding": - // Do nothing, we already did - break; - case "content-id": - $params['cid'] = str_replace('<', '', str_replace('>', '', $v)); - break; - default: - $params[$k] = $v; - break; - } - } - - // If not exist body, the part will be multipart/alternative, so we don't add encoding - if (!isset($params['encoding']) && isset($part->body)) { - $params['encoding'] = 'base64'; - } - // We could not have body; recursive messages - $new_part = $email->addSubPart(isset($part->body) ? $part->body : "", $params); - unset($params); - } - - // return the new part - return $new_part; - } \ No newline at end of file diff --git a/sources/testing/testing-imap.php b/sources/testing/testing-imap.php deleted file mode 100644 index c144e9f..0000000 --- a/sources/testing/testing-imap.php +++ /dev/null @@ -1,8 +0,0 @@ -\n", $mail); -printf("EMPTY <%b>\n", empty($mail)); \ No newline at end of file diff --git a/sources/testing/testing-imap_date.php b/sources/testing/testing-imap_date.php deleted file mode 100644 index 2628439..0000000 --- a/sources/testing/testing-imap_date.php +++ /dev/null @@ -1,51 +0,0 @@ -Nmsgs}",0); -foreach ($result as $overview) { - echo "#{$overview->msgno} ({$overview->date}) - From: {$overview->from} {$overview->subject}\n"; - if (inside_cutoffdate($limit, $overview->uid, $mbox)) - echo "INSIDE\n"; - else - echo "OUTSIDE\n"; -} -imap_close($mbox); - -function inside_cutoffdate($cutoffdate, $id, $mbox) { - printf("Checking if the messages is withing the cutoffdate %d, %s", $cutoffdate, $id); - $is_inside = false; - - if ($cutoffdate == 0) { - // No cutoffdate, all the messages are in range - $is_inside = true; - printf("No cutoffdate, all the messages are in range"); - } - else { - $overview = imap_fetch_overview($mbox, $id, FT_UID); - if (is_array($overview)) { - if (isset($overview[0]->date)) { - $epoch_sent = strtotime($overview[0]->date); - $is_inside = ($cutoffdate <= $epoch_sent); - } - else { - // No sent date defined, that's a buggy message but we will think that the message is in range - $is_inside = true; - printf("No sent date defined, that's a buggy message but we will think that the message is in range"); - } - } - else { - // No overview, maybe the message is no longer there - $is_inside = false; - printf("No overview, maybe the message is no longer there"); - } - } - - return $is_inside; -} \ No newline at end of file diff --git a/sources/testing/testing-imap_folder_list.php b/sources/testing/testing-imap_folder_list.php deleted file mode 100644 index 182562d..0000000 --- a/sources/testing/testing-imap_folder_list.php +++ /dev/null @@ -1,188 +0,0 @@ -name, strlen($server)); - printf("Evaluating %s\n", $imapid); - - GetFolder($imapid); - - $fhir = explode($val->delimiter, $imapid); - if (count($fhir) > 1) { - if (defined('IMAP_FOLDER_PREFIX') && strlen(IMAP_FOLDER_PREFIX) > 0) { - if (strcasecmp($fhir[0], IMAP_FOLDER_PREFIX) == 0) { -// printf("Removing prefix\n"); - // Discard prefix - array_shift($fhir); - } - } - - if (count($fhir) == 1) { - $box["mod"] = $fhir[0]; - $box["parent"] = "0"; - } - else { -// printf("Subfolder\n"); - getModAndParentNames($fhir, $box["mod"], $imapparent); - if ($imapparent === null) { - $box["parent"] = "0"; - } - else { - $box["parent"] = $imapparent; - } - } - } - else { - $box["mod"] = $imapid; - $box["parent"] = "0"; - } - $folders[] = $box; - } - -// print_r($folders); -} -else { - printf("imap_list failed: %s\n", imap_last_error()); -} - -imap_close($mbox); - - -function getModAndParentNames($fhir, &$displayname, &$parent) { - // if mod is already set add the previous part to it as it might be a folder which has delimiter in its name - $displayname = (isset($displayname) && strlen($displayname) > 0) ? $displayname = array_pop($fhir) . getServerDelimiter() . $displayname : array_pop($fhir); - $parent = implode(getServerDelimiter(), $fhir); - - if (count($fhir) == 1 || checkIfIMAPFolder($parent)) { - return; - } - //recursion magic - getModAndParentNames($fhir, $displayname, $parent); -} - -function getServerDelimiter() { - $list = @imap_getmailboxes($mbox, $server, "*"); - if (is_array($list) && count($list) > 0) { - // get the delimiter from the first folder - $delimiter = $list[0]->delimiter; - } else { - // default - $delimiter = "."; - } - return $delimiter; -} - -function checkIfIMAPFolder($folderName) { - $folder_name = $folderName; - if (defined(IMAP_FOLDER_PREFIX) && strlen(IMAP_FOLDER_PREFIX) > 0) { - // TODO: We don't care about the inbox exception with the prefix, because we won't check inbox - $folder_name = IMAP_FOLDER_PREFIX . getServerDelimiter() . $folder_name; - } - $list_subfolders = @imap_list($mbox, $server, $folder_name); - return is_array($list_subfolders); -} - - -function GetFolder($imapid) { - $folder = array(); - - // explode hierarchy - $fhir = explode(getServerDelimiter(), $imapid); - - if (strcasecmp($imapid, create_name_folder(IMAP_FOLDER_INBOX)) == 0) { - $folder["parentid"] = "0"; - $folder["displayname"] = "Inbox"; - $folder["type"] = "SYNC_FOLDER_TYPE_INBOX"; - } - else if (strcasecmp($imapid, create_name_folder(IMAP_FOLDER_DRAFT)) == 0) { - $folder["parentid"] = "0"; - $folder["displayname"] = "Drafts"; - $folder["type"] = "SYNC_FOLDER_TYPE_DRAFTS"; - } - else if (strcasecmp($imapid, create_name_folder(IMAP_FOLDER_SENT)) == 0) { - $folder["parentid"] = "0"; - $folder["displayname"] = "Sent"; - $folder["type"] = "SYNC_FOLDER_TYPE_SENTMAIL"; - } - else if (strcasecmp($imapid, create_name_folder(IMAP_FOLDER_TRASH)) == 0) { - $folder["parentid"] = "0"; - $folder["displayname"] = "Trash"; - $folder["type"] = "SYNC_FOLDER_TYPE_WASTEBASKET"; - } - else { - if (defined('IMAP_FOLDER_PREFIX') && strlen(IMAP_FOLDER_PREFIX) > 0) { - if (strcasecmp($fhir[0], IMAP_FOLDER_PREFIX) == 0) { - // Discard prefix - array_shift($fhir); - } - else { - printf("GetFolder('%s'): '%s'; using server delimiter '%s', first part '%s' is not equal to the prefix defined '%s'. Something is wrong with your config.\n", $id, $imapid, getServerDelimiter(), $fhir[0], IMAP_FOLDER_PREFIX); - } - } - - if (count($fhir) == 1) { - $folder["displayname"] = $fhir[0]; - $folder["parentid"] = "0"; - } - else { - getModAndParentNames($fhir, $folder["displayname"], $imapparent); - $folder["displayname"] = $folder["displayname"]; - if ($imapparent === null) { - printf("GetFolder('%s'): '%s'; we didn't found a valid parent name for the folder, but we should... contact the developers for further info\n", $id, $imapid); - $folder["parentid"] = "0"; // We put the folder as root folder, so we see it - } - else { - $folder["parentid"] = $imapparent; - } - } - $folder["type"] = "SYNC_FOLDER_TYPE_USER_MAIL"; - } - -// print_r($folder); -} - -function create_name_folder($folder_name) { - $foldername = $folder_name; - // If we have defined a folder prefix, and it's not empty - if (defined('IMAP_FOLDER_PREFIX') && IMAP_FOLDER_PREFIX != "") { - // If inbox uses prefix or we are not evaluating inbox - if (IMAP_FOLDER_PREFIX_IN_INBOX == true || strcasecmp($foldername, IMAP_FOLDER_INBOX) != 0) { - $foldername = IMAP_FOLDER_PREFIX . getServerDelimiter() . $foldername; - } - } - - return $foldername; -} \ No newline at end of file diff --git a/sources/testing/testing-imap_from.php b/sources/testing/testing-imap_from.php deleted file mode 100644 index c146367..0000000 --- a/sources/testing/testing-imap_from.php +++ /dev/null @@ -1,133 +0,0 @@ -'); - -function get_from_ldap($username, $domain) { - $from = $username; - - $ldap_conn = null; - try { - $ldap_conn = ldap_connect(IMAP_FROM_LDAP_SERVER, IMAP_FROM_LDAP_SERVER_PORT); - if ($ldap_conn) { - printf("Connected to LDAP"); - ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3); - ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0); - $ldap_bind = ldap_bind($ldap_conn, IMAP_FROM_LDAP_USER, IMAP_FROM_LDAP_PASSWORD); - - if ($ldap_bind) { - printf("Authenticated in LDAP"); - $filter = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_LDAP_QUERY)); - printf("Searching From with filter: %s", $filter); - $search = ldap_search($ldap_conn, IMAP_FROM_LDAP_BASE, $filter, unserialize(IMAP_FROM_LDAP_FIELDS)); - $items = ldap_get_entries($ldap_conn, $search); - if ($items['count'] > 0) { - printf("Found entry in LDAP. Generating From"); - $from = IMAP_FROM_LDAP_FROM; - // We get the first object. It's your responsability to make the query unique - foreach (unserialize(IMAP_FROM_LDAP_FIELDS) as $field) { - $from = str_replace('#'.$field, $items[0][$field][0], $from); - } - } - else { - printf("No entry found in LDAP"); - } - } - else { - printf("Not authenticated in LDAP server"); - } - } - else { - printf("Not connected to LDAP server"); - } - } - catch(Exception $ex) { - printf("Error getting From value from LDAP server: %s", $ex); - } - - ldap_close($ldap_conn); - - return $from; -} - -$from = get_from_ldap('fmbiete', 'zpush.org'); -printf("%s\n", $from); - - -define('IMAP_FROM_SQL_DSN', 'mysql:host=xxxxxxx;port=3306;dbname=xxxxxxx'); -define('IMAP_FROM_SQL_USER', 'xxxxxxx'); -define('IMAP_FROM_SQL_PASSWORD', 'xxxxxxx'); -define('IMAP_FROM_SQL_OPTIONS', serialize(array(PDO::ATTR_PERSISTENT => false))); -define('IMAP_FROM_SQL_QUERY', 'select role, sede, email from usuarios where email = "#username@#domain"'); -define('IMAP_FROM_SQL_FIELDS', serialize(array('role', 'sede', 'email'))); -define('IMAP_FROM_SQL_FROM', '#role #sede <#email>'); - -function get_from_sql($username, $domain) { - $from = $username; - - $dbh = $sth = $record = null; - try { - $dbh = new PDO(IMAP_FROM_SQL_DSN, IMAP_FROM_SQL_USER, IMAP_FROM_SQL_PASSWORD, unserialize(IMAP_FROM_SQL_OPTIONS)); - printf("Connected to SQL Database"); - - $sql = str_replace('#username', $username, str_replace('#domain', $domain, IMAP_FROM_SQL_QUERY)); - printf("Searching From with filter: %s", $sql); - $sth = $dbh->prepare($sql); - $sth->execute(); - $record = $sth->fetch(PDO::FETCH_ASSOC); - if ($record) { - printf("Found entry in SQL Database. Generating From"); - $from = IMAP_FROM_SQL_FROM; - foreach (unserialize(IMAP_FROM_SQL_FIELDS) as $field) { - $from = str_replace('#'.$field, $record[$field], $from); - } - } - else { - printf("No entry found in SQL Database"); - } - } - catch(PDOException $ex) { - printf("Error getting From value from SQL Database: %s", $ex); - } - - $dbh = $sth = $record = null; - - return $from; -} - -function encoding_from($from) { - $items = explode("<", $from); - $name = trim($items[0]); - return "=?UTF-8?B?" . base64_encode($name) . "?= <" . $items[1]; -} - -$from = get_from_sql('fmbiete', 'zpush.org'); -printf("%s\n", $from); - -$from = "Francisco Miguel Biete Bañón "; -$encoded_from = encoding_from($from); -printf("%s\n", $encoded_from); - - -function parseAddr($ad) { - $addr_string = ""; - if (isset($ad) && is_array($ad)) { - foreach($ad as $addr) { - if ($addr_string) $addr_string .= ","; - $addr_string .= $addr->mailbox . "@" . $addr->host; - } - } - return $addr_string; -} - -require_once 'vendor/autoload.php'; - -$Mail_RFC822 = new Mail_RFC822(); -$fromaddr = parseAddr($Mail_RFC822->parseAddressList($encoded_from)); -printf("%s\n", $fromaddr); \ No newline at end of file diff --git a/sources/testing/testing-imap_issue_120.php b/sources/testing/testing-imap_issue_120.php deleted file mode 100644 index b161818..0000000 --- a/sources/testing/testing-imap_issue_120.php +++ /dev/null @@ -1,70 +0,0 @@ - 0) ? $displayname = array_pop($fhir).$serverdelimiter.$displayname : array_pop($fhir); - $parent = implode($serverdelimiter, $fhir); - - if (count($fhir) == 1 || checkIfIMAPFolder($parent)) { - return; - } - //recursion magic - getModAndParentNames($fhir, $displayname, $parent); -} - -function getModAndParentNamesWithSharedSupport($fhir, &$displayname, &$parent) { - // PUT IN HERE YOUR SHARED FOLDER PREFIXES - $shared_prefix = [ "#Users", "#Public" ]; - - $serverdelimiter = "/"; - - if (!isset($displayname) || strlen($displayname) == 0) { - if (count($fhir) > 1) { - foreach($shared_prefix as $prefix) { - if (strcasecmp($fhir[0], $prefix) == 0) { - printf("Found shared prefix\n"); - // Remove first element, it's the shared prefix - array_shift($fhir); - $displayname = "SHARED " . $fhir[count($fhir) - 1]; - $parent = implode($serverdelimiter, $fhir); - return; - } - } - } - } - - printf("displayname is: %s\n", $displayname); - // if mod is already set add the previous part to it as it might be a folder which has - // delimiter in its name - $displayname = (isset($displayname) && strlen($displayname) > 0) ? $displayname = array_pop($fhir).$serverdelimiter.$displayname : array_pop($fhir); - $parent = implode($serverdelimiter, $fhir); - - printf("displayname after is: %s\n", $displayname); - - if (count($fhir) == 1 || checkIfIMAPFolder($parent)) { - return; - } - //recursion magic - getModAndParentNames($fhir, $displayname, $parent); -} - -function checkIfIMAPFolder($folderName) { - return true; -} \ No newline at end of file diff --git a/sources/testing/testing-imap_list_155.php b/sources/testing/testing-imap_list_155.php deleted file mode 100644 index d45dc4b..0000000 --- a/sources/testing/testing-imap_list_155.php +++ /dev/null @@ -1,18 +0,0 @@ -name) . "\n"; - } -} else { - echo "imap_list failed: " . imap_last_error() . "\n"; -} - -imap_close($mbox); \ No newline at end of file diff --git a/sources/testing/testing-imap_meeting.php b/sources/testing/testing-imap_meeting.php deleted file mode 100644 index 03ce136..0000000 --- a/sources/testing/testing-imap_meeting.php +++ /dev/null @@ -1,68 +0,0 @@ -ParseFrom($body); - -$props = $ical->GetPropertiesByPath("VEVENT/UID"); -if (count($props) > 0) { - $uid = $props[0]->Value(); - printf("UID: %s\n", $uid); -} -else { - printf("NO UID\n"); -} - -$new_attendees = array(); -$props = $ical->GetPropertiesByPath('VEVENT/ATTENDEE'); -for ($i = 0; $i < count($props); $i++) { - printf("Attendee Mailto '%s' Status '%s'\n", str_ireplace("MAILTO:", "", $props[$i]->Value()), $props[$i]->Parameters()["PARTSTAT"]); -} - -$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "RSVP", null); - -// MODIFICATIONS - // METHOD -$ical->SetPValue("METHOD", "REPLY"); - //ATTENDEE -$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", "ACCEPTED"); -$ical->SetCPParameterValue("VEVENT", "ATTENDEE", "PARTSTAT", "TENTATIVE", "MAILTO:user2@zpush.org"); - - -printf("%s\n", $ical->Render()); - - -$mail = new Mail_mimePart(); -$headers = array("MIME-version" => "1.0", - "From" => $mail->encodeHeader("from", "Pedro Picapiedra ", "UTF-8"), - "To" => $mail->encodeHeader("to", "Pablo Marmol ", "UTF-8"), - "Date" => gmdate("D, d M Y H:i:s", time())." GMT", - "Subject" => $mail->encodeHeader("subject", "This is a subject", "UTF-8"), - "Content-class" => "urn:content-classes:calendarmessage", - "Content-transfer-encoding" => "8BIT"); -$mail = new Mail_mimePart($ical->Render(), array("content_type" => "text/calendar; method=REPLY; charset=UTF-8", "headers" => $headers)); - -$message = ""; -$encoded_mail = $mail->encode(); -foreach ($encoded_mail["headers"] as $k => $v) { - $message .= $k . ": " . $v . "\r\n"; -} -$message .= "\r\n" . $encoded_mail["body"] . "\r\n"; - -printf("%s\n", $message); - - -$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID"); -if (count($props) > 0) { - $tzid = $props[0]->Value(); - printf("TZID %s\n", $props[0]->Value()); -} -print_r(TimezoneUtil::GetFullTZFromTZName($tzid)); \ No newline at end of file diff --git a/sources/testing/testing-imap_meeting_method.php b/sources/testing/testing-imap_meeting_method.php deleted file mode 100644 index 5c45dab..0000000 --- a/sources/testing/testing-imap_meeting_method.php +++ /dev/null @@ -1,25 +0,0 @@ -ParseFrom($body); - - $props = $ical->GetPropertiesByPath("VCALENDAR/METHOD"); - if (count($props) > 0) { - printf("METHOD %s\n", $props[0]->Value()); - } -} - - -testing_get_method('testing/samples/meeting_request.txt'); -testing_get_method('testing/samples/meeting_request_rim.txt'); -testing_get_method('testing/samples/meeting_reply_rim.txt'); \ No newline at end of file diff --git a/sources/testing/testing-imap_meeting_tzid.php b/sources/testing/testing-imap_meeting_tzid.php deleted file mode 100644 index 0918380..0000000 --- a/sources/testing/testing-imap_meeting_tzid.php +++ /dev/null @@ -1,33 +0,0 @@ -ParseFrom($body); - -$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID"); -if (count($props) > 0) { - $tzid = $props[0]->Value(); - printf("TZID %s\n", $props[0]->Value()); -} -print_r(TimezoneUtil::GetFullTZFromTZName($tzid)); - - -$body = file_get_contents('testing/samples/meeting_request_rim.txt'); - -$ical = new iCalComponent(); -$ical->ParseFrom($body); - -$props = $ical->GetPropertiesByPath("VTIMEZONE/TZID"); -if (count($props) > 0) { - $tzid = $props[0]->Value(); - printf("TZID %s\n", $props[0]->Value()); -} -print_r(TimezoneUtil::GetFullTZFromTZName($tzid)); \ No newline at end of file diff --git a/sources/testing/testing-imap_overview.php b/sources/testing/testing-imap_overview.php deleted file mode 100644 index 230d2ac..0000000 --- a/sources/testing/testing-imap_overview.php +++ /dev/null @@ -1,35 +0,0 @@ -date)) { - printf("%s\n", $overview->date); - printf("%s\n", cleanupDate($overview->date)); - } - if (isset($overview->udate)) { - printf("%s\n", $overview->udate); - } -} -imap_close($mbox); - - -function cleanupDate($receiveddate) { - if (is_array($receiveddate)) { - // Header Date could be repeated in the message, we only check the first - $receiveddate = $receiveddate[0]; - } - printf("%s\n", preg_replace("/\(.*\)/", "", $receiveddate)); - printf("%s\n", preg_replace('/\(.*\)/', "", $receiveddate)); - printf("%s\n", preg_replace("/\\(.*\\)/", "", $receiveddate)); - $receiveddate = strtotime(preg_replace("/\(.*\)/", "", $receiveddate)); - if ($receiveddate == false || $receiveddate == -1) { - printf("cleanupDate() : Received date is false. Message might be broken."); - return null; - } - - return $receiveddate; -} \ No newline at end of file diff --git a/sources/testing/testing-imap_smtp.php b/sources/testing/testing-imap_smtp.php deleted file mode 100644 index 485a64b..0000000 --- a/sources/testing/testing-imap_smtp.php +++ /dev/null @@ -1,53 +0,0 @@ - 'smtp.zpush.org', 'port' => 25, 'auth' => true, "username" => "fmbiete", "password" => "password_account", "debug" => true, "pipelining" => true); -$toaddr = "fmbiete@zpush.org"; -$headers = array('Subject' => 'Testing SMTP', 'From' => 'fmbiete@zpush.org', 'Return-Path' => 'fmbiete@zpush.org', 'To' => 'fmbiete@zpush.org', 'Cc' => 'fmbiete@zpush.org,fmbiete@zpush.net', 'Bcc' => array('fmbiete2@zpush.org', 'fmbiete2@zpush.net', 'fmbiete@zpush.org')); -$body = "This is a test"; - - -if (is_array($toaddr)) { - $recipients = $toaddr; -} -else { - $recipients = array($toaddr); -} - -// Cc and Bcc headers are sent, but we need to make sure that the recipient list contains them -foreach (array("CC", "cc", "Cc", "BCC", "Bcc", "bcc") as $key) { - if (!empty($headers[$key])) { - if (is_array($headers[$key])) { - $recipients = array_merge($recipients, $headers[$key]); - } - else { - $recipients[] = $headers[$key]; - } - } -} - -ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->sendMessage(): SendingMail with %s", "smtp")); -$mail =& Mail::factory("smtp", $imap_smtp_params); -$send = $mail->send($recipients, $headers, $body); -ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->sendMessage(): send return value %s", $send)); - -if ($send !== true) { - ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): The email could not be sent")); -} \ No newline at end of file diff --git a/sources/testing/testing-issue_164.php b/sources/testing/testing-issue_164.php deleted file mode 100644 index 24256d5..0000000 --- a/sources/testing/testing-issue_164.php +++ /dev/null @@ -1,27 +0,0 @@ -'; - -printf("Encoded from [%s]\n", $encoded_from); - -$mimeDecode = new Mail_mimeDecode(); -$decoded_from = $mimeDecode->_decodeHeader($encoded_from); -printf("Decoded from [%s]\n", $decoded_from); - -$Mail_RFC822 = new Mail_RFC822(); -$parsed = $Mail_RFC822->parseAddressList($decoded_from); - -printf("Empty if wrong email address\n"); -print_r($parsed); \ No newline at end of file diff --git a/sources/testing/testing-mime-mail-parse.php b/sources/testing/testing-mime-mail-parse.php deleted file mode 100644 index 6a8bda4..0000000 --- a/sources/testing/testing-mime-mail-parse.php +++ /dev/null @@ -1,57 +0,0 @@ -decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); - $handle = fopen($new_file, "w"); - fwrite($handle, build_mime_message($message)); - fclose($handle); - - foreach ($message->headers as $k => $v) { - if (is_array($v)) { - foreach ($v as $vk => $vv) { - printf("Header <%s> <%s> <%s>\n", $k, $vk, $vv); - } - } - else { - printf("Header <%s> <%s>\n", $k, $v); - } - } - - $text = $html = ""; - Mail_mimeDecode::getBodyRecursive($message, "plain", $text); - Mail_mimeDecode::getBodyRecursive($message, "html", $html); - - printf("TEXT Body <%s>\n", $text); - - printf("HTML Body <%s>\n", $html); -} \ No newline at end of file diff --git a/sources/testing/testing-mime-split.php b/sources/testing/testing-mime-split.php deleted file mode 100644 index 66256e7..0000000 --- a/sources/testing/testing-mime-split.php +++ /dev/null @@ -1,22 +0,0 @@ -getSendArray(); -if ($parts === false) { - printf("ERROR splitting message\n"); -} -else { - list($recipents,$headers,$body) = $parts; - printf("RECIPIENTS\n"); - print_r($recipents); - printf("\nHEADERS\n"); - print_r($headers); - printf("\nBODY\n"); - print_r($body); - printf("\n"); - //$mail = Mail::factory('smtp'); - //$mail->send($recipents,$headers,$body); -} \ No newline at end of file diff --git a/sources/testing/testing-mime.php b/sources/testing/testing-mime.php deleted file mode 100644 index cad341f..0000000 --- a/sources/testing/testing-mime.php +++ /dev/null @@ -1,179 +0,0 @@ -decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'charset' => 'utf-8')); - - -function addSubPart(&$email, $part) { - //http://tools.ietf.org/html/rfc4021 - $new_part = null; - $params = array(); - if (isset($part)) { - if (isset($part->ctype_primary)) { - $params['content_type'] = $part->ctype_primary; - } - if (isset($part->ctype_secondary)) { - $params['content_type'] .= '/' . $part->ctype_secondary; - } - if (isset($part->ctype_parameters)) { - foreach ($part->ctype_parameters as $k => $v) { - if(strcasecmp($k, 'boundary') != 0) { - $params['content_type'] .= '; ' . $k . '=' . $v; - } - } - } - if (isset($part->disposition)) { - $params['disposition'] = $part->disposition; - } - //FIXME: dfilename => filename - if (isset($part->d_parameters)) { - foreach ($part->d_parameters as $k => $v) { - $params[$k] = $v; - } - } - foreach ($part->headers as $k => $v) { - switch($k) { - case "content-description": - $params['description'] = $v; - break; - case "content-type": - case "content-disposition": - case "content-transfer-encoding": - // Do nothing, we already did - break; - case "content-id": - $params['cid'] = str_replace('<', '', str_replace('>', '', $v)); - break; - default: - $params[$k] = $v; - break; - } - } - - // If not exist body, the part will be multipart/alternative, so we don't add encoding - if (!isset($params['encoding']) && isset($part->body)) { - $params['encoding'] = 'base64'; - } - // We could not have body; recursive messages - $new_part = $email->addSubPart(isset($part->body) ? $part->body : "", $params); - unset($params); - } - - // return the new part - return $new_part; -} - -function fixCharsetAndAddSubParts(&$email, $part) { - if (isset($part)) { - $new_part = null; - if (isset($part->ctype_parameters['charset'])) { - $part->ctype_parameters['charset'] = 'UTF-8'; - $new_part = addSubPart($email, $part); - } - else { - $new_part = addSubPart($email, $part); - } - - if (isset($part->parts)) { - foreach ($part->parts as $subpart) { - fixCharsetAndAddSubParts($new_part, $subpart); - } - } - } -} - - -$boundary = '=_' . md5(rand() . microtime()); -$mimeHeaders = Array(); -$mimeHeaders['headers'] = Array(); -$is_mime = false; -foreach ($message->headers as $key => $value) { - switch($key) { - case 'content-type': - $new_value = $message->ctype_primary . "/" . $message->ctype_secondary; - $is_mime = (strcasecmp($message->ctype_primary, 'multipart') == 0); - - foreach ($message->ctype_parameters as $ckey => $cvalue) { - switch($ckey) { - case 'charset': - $new_value .= '; charset="UTF-8"'; - break; - case 'boundary': - // Do nothing, we are encoding also the headers - break; - default: - $new_value .= '; ' . $ckey . '="' . $cvalue . '"'; - break; - } - } - - $mimeHeaders['content_type'] = $new_value; - break; - case 'content-transfer-encoding': - if (strcasecmp($value, "base64") == 0 || strcasecmp($value, "binary") == 0) { - $mimeHeaders['encoding'] = "base64"; - } - else { - $mimeHeaders['encoding'] = "8bit"; - } - break; - case 'content-id': - $mimeHeaders['cid'] = $value; - break; - case 'content-location': - $mimeHeaders['location'] = $value; - break; - case 'content-disposition': - $mimeHeaders['disposition'] = $value; - break; - case 'content-description': - $mimeHeaders['description'] = $value; - break; - default: - if (is_array($value)) { - foreach($value as $v) { - $mimeHeaders['headers'][$key] = $v; - } - } - else { - $mimeHeaders['headers'][$key] = $value; - } - break; - } -} - -$finalEmail = new Mail_mimePart(isset($message->body) ? $message->body : "", $mimeHeaders); -if (isset($message->parts)) { - foreach ($message->parts as $part) { - fixCharsetAndAddSubParts($finalEmail, $part); - } -} -$finalEmail = $finalEmail->encode($boundary); -$headers = ""; -foreach ($finalEmail['headers'] as $key => $value) { - $headers .= "$key: $value\n"; -} - -if ($is_mime) { - echo "$headers\nThis is a multi-part message in MIME format.\n".$finalEmail['body']; -} -else { - echo "$headers\n".$finalEmail['body']; -} - -unset($headers); -unset($mimeHeaders); -unset($finalEmail); -unset($message); -unset($mobj); -unset($mail); \ No newline at end of file diff --git a/sources/testing/testing-mime_preview.php b/sources/testing/testing-mime_preview.php deleted file mode 100644 index a65d3a2..0000000 --- a/sources/testing/testing-mime_preview.php +++ /dev/null @@ -1,24 +0,0 @@ -decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); -unset($mobj); - -$previewText = ""; -Mail_mimeDecode::getBodyRecursive($message, "plain", $previewText, true); -if (strlen($previewText) == 0) { - printf("No Plain part found\n"); - Mail_mimeDecode::getBodyRecursive($message, "html", $previewText, true); - $previewText = Utils::ConvertHtmlToText($previewText); -} -printf("%s\n", Utils::Utf8_truncate($previewText, 250)); \ No newline at end of file diff --git a/sources/testing/testing-mimetype.php b/sources/testing/testing-mimetype.php deleted file mode 100644 index cbf13dc..0000000 --- a/sources/testing/testing-mimetype.php +++ /dev/null @@ -1,38 +0,0 @@ -connect(IPC_REDIS_IP, IPC_REDIS_PORT); -printf("Connected? %s\n", $connected); - -$keys = $redis->keys("ZP_TOP|" . "*"); -print_r($keys); -printf("Keys is_array? %d, Count %d\n", is_array($keys), count($keys)); - -$values = $redis->mGet($keys); -print_r($values); -printf("Values is_array? %d, Count %d\n", is_array($values), count($values)); diff --git a/sources/testing/testing-rfc822_199.php b/sources/testing/testing-rfc822_199.php deleted file mode 100644 index 6b210fb..0000000 --- a/sources/testing/testing-rfc822_199.php +++ /dev/null @@ -1,15 +0,0 @@ - (A comment), ted@example.com (Ted Bloggs), Barney;'; -$structure = Mail_RFC822::parseAddressList($address_string, 'example.com', true); -// print_r($structure); - -$address_string = 'undisclosed-recipients:;'; -$structure = Mail_RFC822::parseAddressList($address_string); -print_r($structure); - -$address_string = 'fmbiete@zpush.org'; -$structure = Mail_RFC822::parseAddressList($address_string); -// print_r($structure); diff --git a/sources/testing/testing-ternary.php b/sources/testing/testing-ternary.php deleted file mode 100644 index fcc04b7..0000000 --- a/sources/testing/testing-ternary.php +++ /dev/null @@ -1,28 +0,0 @@ - ] []' % __file__ -print 'If username is omitted, all users are scanned' -raw_input("Press to continue or CTRL-C to stop") - -s = OpenECSession('SYSTEM', '', 'file:///var/run/zarafa') - -sslkey_file = None -sslkey_pass = None -if len(sys.argv) > 1: - (sslkey_file, sslkey_pass) = sys.argv[1:3] - -if len(sys.argv) > 3: - users = [sys.argv[3]] -else: - users = GetUserList(s) - -for username in users: - print 'Processing user %s' % username - - try: - s = OpenECSession(username, '', 'file:///var/run/zarafa', sslkey_file = sslkey_file, sslkey_pass = sslkey_pass) - - st = GetDefaultStore(s) - - - ab = s.OpenAddressBook(0, None, 0) - identity = s.QueryIdentity() - gabid = ab.GetDefaultDir() - gabcontainer = ab.OpenEntry(gabid, None, 0) - gab = gabcontainer.GetContentsTable(0) - gab.SetColumns([PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ENTRYID, PR_SEARCH_KEY, PR_ADDRTYPE], 0) - - root = st.OpenEntry(None, None, MAPI_MODIFY) - calid = root.GetProps([PR_IPM_APPOINTMENT_ENTRYID], 0)[0] - if calid.ulPropTag != PR_IPM_APPOINTMENT_ENTRYID: - print 'User has no calendar' - continue - - cal = st.OpenEntry(calid.Value, None, 0) - - t = cal.GetContentsTable(0) - - # Restrict to meetings only (AppointmentStateFlags >= 1) - - t.Restrict(SAndRestriction([ - SOrRestriction([ - SPropertyRestriction(RELOP_EQ, 0x8023000b, SPropValue(0x8023000b, True)), # Recurring OR - SPropertyRestriction(RELOP_GE, 0x800e0040, SPropValue(0x800e0040, unixtime(time.time() - 1*7*24*60*60))) # Starts after now()-'1 week' - ]) - ]), 0) - t.SetColumns([PR_ENTRYID], 0) - - rows = t.QueryRows(-1, 0) - - for row in rows: - modified = False - - message = st.OpenEntry(row[0].Value, None, MAPI_MODIFY) - - subject = message.GetProps([PR_SUBJECT], 0)[0].Value - nameprops = message.GetProps([PR_SENT_REPRESENTING_ENTRYID, 0x80180003], 0) - - prevstatus = nameprops[1].Value - - if nameprops[0].Value == identity and prevstatus != 1: - print "User is organizer of", "'"+subject+"'", "setting correct flag." - message.SetProps([SPropValue(0x80180003, 1)]) - message.SaveChanges(0) - elif nameprops[0].Value != identity and prevstatus == 1: - print "User is attendee of", "'"+subject+"'", "setting correct flag." - message.SetProps([SPropValue(0x80180003, 5)]) - message.SaveChanges(0) - else: - print "Correct property set for", "'"+subject+"'", "skipping." - - except MAPIError, e: - print e - pass diff --git a/sources/tools/migrate-2.0.x-2.1.0.php b/sources/tools/migrate-2.0.x-2.1.0.php deleted file mode 100644 index 6eaa140..0000000 --- a/sources/tools/migrate-2.0.x-2.1.0.php +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/php -. -* -* Consult LICENSE file for details -************************************************/ - -// Please adjust to match your z-push installation directory, usually /usr/share/z-push -define('ZPUSH_BASE_PATH', "../src"); - - - -/************************************************ - * MAIN -*/ -try { - if (php_sapi_name() != "cli") - die(sprintf("This script should not be called in a browser. Called from: %s", php_sapi_name())); - - if (!defined('ZPUSH_BASE_PATH') || !file_exists(ZPUSH_BASE_PATH . "/config.php")) - die("ZPUSH_BASE_PATH not set correctly or no config.php file found\n"); - - define('BASE_PATH_CLI', ZPUSH_BASE_PATH ."/"); - set_include_path(get_include_path() . PATH_SEPARATOR . ZPUSH_BASE_PATH); - - include('lib/core/zpushdefs.php'); - include('lib/core/zpush.php'); - include('lib/core/zlog.php'); - include('lib/core/statemanager.php'); - include('lib/core/stateobject.php'); - include('lib/core/asdevice.php'); - include('lib/core/interprocessdata.php'); - include('lib/exceptions/exceptions.php'); - include('lib/utils/utils.php'); - include('lib/request/request.php'); - include('lib/request/requestprocessor.php'); - include('lib/interface/ibackend.php'); - include('lib/interface/ichanges.php'); - include('lib/interface/iexportchanges.php'); - include('lib/interface/iimportchanges.php'); - include('lib/interface/isearchprovider.php'); - include('lib/interface/istatemachine.php'); - include('config.php'); - - ZPush::CheckConfig(); - $migrate = new StateMigrator20xto210(); - - if (!$migrate->MigrationNecessary()) - echo "Migration script was run before and eventually no migration is necessary. Rerunning checks\n"; - - $migrate->DoMigration(); -} -catch (ZPushException $zpe) { - die(get_class($zpe) . ": ". $zpe->getMessage() . "\n"); -} - -echo "terminated\n"; - - -class StateMigrator20xto210 { - const FROMVERSION = "1"; // IStateMachine::STATEVERSION_01 - const TOVERSION = "2"; // IStateMachine::STATEVERSION_02 - - private $sm; - - /** - * Constructor - */ - public function StateMigrator20xto210() { - $this->sm = false; - } - - /** - * Checks if the migration is necessary - * - * @access public - * @throws FatalMisconfigurationException - * @throws FatalNotImplementedException - * @return boolean - */ - public function MigrationNecessary() { - try { - $this->sm = ZPush::GetStateMachine(); - } - catch (HTTPReturnCodeException $e) { - echo "Check states: states versions do not match and need to be migrated\n\n"; - - // we just try to get the statemachine again - // the exception is only thrown the first time - $this->sm = ZPush::GetStateMachine(); - } - - if (!$this->sm) - throw new FatalMisconfigurationException("Could not get StateMachine from ZPush::GetStateMachine()"); - - if (!($this->sm instanceof FileStateMachine)) { - throw new FatalNotImplementedException("This conversion script is only able to convert states of the FileStateMachine"); - } - - if ($this->sm->GetStateVersion() == ZPush::GetLatestStateVersion()) - return false; - - if ($this->sm->GetStateVersion() !== self::FROMVERSION || ZPush::GetLatestStateVersion() !== self::TOVERSION) - throw new FatalNotImplementedException(sprintf("This script only converts from state version %d to %d. Currently the system is on %d and should go to %d. Please contact support.", self::FROMVERSION, self::TOVERSION, $this->sm->GetStateVersion(), ZPush::GetLatestStateVersion())); - - // do migration - return true; - } - - /** - * Execute the migration - * - * @access public - * @return true - */ - public function DoMigration() { - // go through all files - $files = glob(STATE_DIR. "/*/*/*", GLOB_NOSORT); - $filetotal = count($files); - $filecount = 0; - $rencount = 0; - $igncount = 0; - - foreach ($files as $file) { - $filecount++; - $newfile = strtolower($file); - echo "\033[1G"; - - if ($file !== $newfile) { - $rencount++; - rename ($file, $newfile); - } - else - $igncount++; - - printf("Migrating file %d/%d\t%s", $filecount, $filetotal, $file); - } - echo "\033[1G". sprintf("Migrated total of %d files, %d renamed and %d ignored (as already correct)%s\n\n", $filetotal, $rencount, $igncount, str_repeat(" ", 50)); - - // get all states of synchronized devices - $alldevices = $this->sm->GetAllDevices(false); - foreach ($alldevices as $devid) { - $lowerDevid = strtolower($devid); - - echo "Processing device: ". $devid . "\t"; - - // update device data - $devState = ZPush::GetStateMachine()->GetState($lowerDevid, IStateMachine::DEVICEDATA); - $newdata = array(); - foreach ($devState->devices as $user => $dev) { - if (!isset($dev->deviceidOrg)) - $dev->deviceidOrg = $dev->deviceid; - - $dev->deviceid = strtolower($dev->deviceid); - $dev->useragenthistory = array_unique($dev->useragenthistory); - $newdata[$user] = $dev; - } - $devState->devices = $newdata; - $this->sm->SetState($devState, $lowerDevid, IStateMachine::DEVICEDATA); - - // go through the users again: device was updated sucessfully, now we change the global user <-> device link - foreach ($devState->devices as $user => $dev) { - printf("\n\tUn-linking %s with old device id %s", $user, $dev->deviceidOrg); - $this->sm->UnLinkUserDevice($user, $dev->deviceidOrg); - printf("\n\tRe-linking %s with new device id %s", $user, $dev->deviceid); - $this->sm->LinkUserDevice($user, $dev->deviceid); - } - - echo "\n\tcompleted\n"; - } - echo "\nSetting new StateVersion\n"; - $this->sm->SetStateVersion(self::TOVERSION); - echo "Migration completed!\n\n"; - - return true; - } -} diff --git a/sources/tools/printwbxml.php b/sources/tools/printwbxml.php deleted file mode 100644 index f430070..0000000 --- a/sources/tools/printwbxml.php +++ /dev/null @@ -1,84 +0,0 @@ -. - * - * Consult LICENSE file for details - ************************************************/ - -if (count($argv) < 2) { - die("\tUsage: printwbmxl.php WBXML-INPUT-HERE\n\n"); -} -$wbxml64 = $argv[1]; - -// include the stuff we need -include_once('../../src/lib/utils/stringstreamwrapper.php'); -include_once('../../src/lib/wbxml/wbxmldefs.php'); -include_once('../../src/lib/wbxml/wbxmldecoder.php'); -include_once('../../src/lib/wbxml/wbxmlencoder.php'); - -// minimal definitions & log to stdout overwrite -define('WBXML_DEBUG', true); -define("LOGLEVEL_WBXML", "wbxml"); -define("LOGLEVEL_DEBUG", "debug"); -class ZLog { - static public function Write($level, $msg, $truncate = false) { - // we only care about the wbxml - if ($level == "wbxml") { - if (substr($msg,0,1) == "I") { - echo substr($msg,1) . "\n"; - } - else { - echo $msg . "\n"; - } - } - } -} - -// setup -$wxbml = StringStreamWrapper::Open($wbxml64); -$base64filter = stream_filter_append($wxbml, 'convert.base64-decode'); -$decoder = new WBXMLDecoder($wxbml); -if (! $decoder->IsWBXML()) { - die("input is not WBXML as base64\n\n"); -} - -echo "\n"; -// read everything and log it -$decoder->readRemainingData(); -echo "\n"; diff --git a/sources/vendor/autoload.php b/sources/vendor/autoload.php deleted file mode 100644 index c1e7b20..0000000 --- a/sources/vendor/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0 class loader - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - */ -class ClassLoader -{ - // PSR-4 - private $prefixLengthsPsr4 = array(); - private $prefixDirsPsr4 = array(); - private $fallbackDirsPsr4 = array(); - - // PSR-0 - private $prefixesPsr0 = array(); - private $fallbackDirsPsr0 = array(); - - private $useIncludePath = false; - private $classMap = array(); - - private $classMapAuthoritative = false; - - public function getPrefixes() - { - if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); - } - - return array(); - } - - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - (array) $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - (array) $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-0 base directories - * @param bool $prepend Whether to prepend the directories - * - * @throws \InvalidArgumentException - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - (array) $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - (array) $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * - * @throws \InvalidArgumentException - */ - public function setPsr4($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Turns off searching the prefix and fallback directories for classes - * that have not been registered with the class map. - * - * @param bool $classMapAuthoritative - */ - public function setClassMapAuthoritative($classMapAuthoritative) - { - $this->classMapAuthoritative = $classMapAuthoritative; - } - - /** - * Should class lookup fail if not found in the current class map? - * - * @return bool - */ - public function isClassMapAuthoritative() - { - return $this->classMapAuthoritative; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - includeFile($file); - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 - if ('\\' == $class[0]) { - $class = substr($class, 1); - } - - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - if ($this->classMapAuthoritative) { - return false; - } - - $file = $this->findFileWithExtension($class, '.php'); - - // Search for Hack files if we are running on HHVM - if ($file === null && defined('HHVM_VERSION')) { - $file = $this->findFileWithExtension($class, '.hh'); - } - - if ($file === null) { - // Remember that this class does not exist. - return $this->classMap[$class] = false; - } - - return $file; - } - - private function findFileWithExtension($class, $ext) - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { - if (0 === strpos($class, $prefix)) { - foreach ($this->prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - } -} - -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; -} diff --git a/sources/vendor/composer/autoload_classmap.php b/sources/vendor/composer/autoload_classmap.php deleted file mode 100644 index dd8088f..0000000 --- a/sources/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,155 +0,0 @@ - $baseDir . '/lib/core/asdevice.php', - 'Auth_SASL' => $baseDir . '/include/Auth/SASL.php', - 'Auth_SASL_Anonymous' => $baseDir . '/include/Auth/SASL/Anonymous.php', - 'Auth_SASL_Common' => $baseDir . '/include/Auth/SASL/Common.php', - 'Auth_SASL_CramMD5' => $baseDir . '/include/Auth/SASL/CramMD5.php', - 'Auth_SASL_DigestMD5' => $baseDir . '/include/Auth/SASL/DigestMD5.php', - 'Auth_SASL_External' => $baseDir . '/include/Auth/SASL/External.php', - 'Auth_SASL_Login' => $baseDir . '/include/Auth/SASL/Login.php', - 'Auth_SASL_Plain' => $baseDir . '/include/Auth/SASL/Plain.php', - 'Auth_SASL_SCRAM' => $baseDir . '/include/Auth/SASL/SCRAM.php', - 'AuthenticationRequiredException' => $baseDir . '/lib/exceptions/authenticationrequiredexception.php', - 'Backend' => $baseDir . '/lib/default/backend.php', - 'BackendDiff' => $baseDir . '/lib/default/diffbackend/diffbackend.php', - 'BodyPreference' => $baseDir . '/lib/core/bodypreference.php', - 'CalDAVClient' => $baseDir . '/include/z_caldav.php', - 'CalendarInfo' => $baseDir . '/include/z_caldav.php', - 'ChangesMemoryWrapper' => $baseDir . '/lib/core/changesmemorywrapper.php', - 'ContentParameters' => $baseDir . '/lib/core/contentparameters.php', - 'DeviceManager' => $baseDir . '/lib/core/devicemanager.php', - 'DiffState' => $baseDir . '/lib/default/diffbackend/diffstate.php', - 'ExportChangesDiff' => $baseDir . '/lib/default/diffbackend/exportchangesdiff.php', - 'FatalException' => $baseDir . '/lib/exceptions/fatalexception.php', - 'FatalMisconfigurationException' => $baseDir . '/lib/exceptions/fatalmisconfigurationexception.php', - 'FatalNotImplementedException' => $baseDir . '/lib/exceptions/fatalnotimplementedexception.php', - 'FileStateMachine' => $baseDir . '/lib/default/filestatemachine.php', - 'FolderChange' => $baseDir . '/lib/request/folderchange.php', - 'FolderSync' => $baseDir . '/lib/request/foldersync.php', - 'GetAttachment' => $baseDir . '/lib/request/getattachment.php', - 'GetHierarchy' => $baseDir . '/lib/request/gethierarchy.php', - 'GetItemEstimate' => $baseDir . '/lib/request/getitemestimate.php', - 'HTTPReturnCodeException' => $baseDir . '/lib/exceptions/httpreturncodeexception.php', - 'HierarchyCache' => $baseDir . '/lib/core/hierarchycache.php', - 'IBackend' => $baseDir . '/lib/interface/ibackend.php', - 'IChanges' => $baseDir . '/lib/interface/ichanges.php', - 'IExportChanges' => $baseDir . '/lib/interface/iexportchanges.php', - 'IImportChanges' => $baseDir . '/lib/interface/iimportchanges.php', - 'ILoopDetection' => $baseDir . '/lib/interface/iloopdetection.php', - 'IPingTracking' => $baseDir . '/lib/interface/ipingtracking.php', - 'ISearchProvider' => $baseDir . '/lib/interface/isearchprovider.php', - 'IStateMachine' => $baseDir . '/lib/interface/istatemachine.php', - 'ITopCollector' => $baseDir . '/lib/interface/itopcollector.php', - 'ImportChangesDiff' => $baseDir . '/lib/default/diffbackend/importchangesdiff.php', - 'ImportChangesStream' => $baseDir . '/lib/core/streamimporter.php', - 'InterProcessData' => $baseDir . '/lib/ipc/shm/interprocessdata.php', - 'InterProcessRedis' => $baseDir . '/lib/ipc/redis/InterProcessRedis.php', - 'InterProcessStorage' => $baseDir . '/lib/ipc/interprocessstorage.php', - 'ItemOperations' => $baseDir . '/lib/request/itemoperations.php', - 'LoopDetection' => $baseDir . '/lib/ipc/shm/loopdetection.php', - 'LoopDetectionRedis' => $baseDir . '/lib/ipc/redis/LoopDetectionRedis.php', - 'Mail' => $baseDir . '/include/Mail.php', - 'Mail_RFC822' => $baseDir . '/include/z_RFC822.php', - 'Mail_mail' => $baseDir . '/include/Mail/mail.php', - 'Mail_mimeDecode' => $baseDir . '/include/mimeDecode.php', - 'Mail_mimePart' => $baseDir . '/include/mimePart.php', - 'Mail_sendmail' => $baseDir . '/include/Mail/sendmail.php', - 'Mail_smtp' => $baseDir . '/include/Mail/smtp.php', - 'MeetingResponse' => $baseDir . '/lib/request/meetingresponse.php', - 'MoveItems' => $baseDir . '/lib/request/moveitems.php', - 'Net_SMTP' => $baseDir . '/include/Net/SMTP.php', - 'Net_Socket' => $baseDir . '/include/Net/Socket.php', - 'NoHierarchyCacheAvailableException' => $baseDir . '/lib/exceptions/nohierarchycacheavailableexception.php', - 'NoPostRequestException' => $baseDir . '/lib/exceptions/nopostrequestexception.php', - 'NotImplementedException' => $baseDir . '/lib/exceptions/notimplementedexception.php', - 'Notify' => $baseDir . '/lib/request/notify.php', - 'Ping' => $baseDir . '/lib/request/ping.php', - 'PingTracking' => $baseDir . '/lib/ipc/shm/pingtracking.php', - 'PingTrackingRedis' => $baseDir . '/lib/ipc/redis/PingTrackingRedis.php', - 'Provisioning' => $baseDir . '/lib/request/provisioning.php', - 'ProvisioningRequiredException' => $baseDir . '/lib/exceptions/provisioningrequiredexception.php', - 'Request' => $baseDir . '/lib/request/request.php', - 'RequestProcessor' => $baseDir . '/lib/request/requestprocessor.php', - 'ResolveRecipients' => $baseDir . '/lib/request/resolverecipients.php', - 'Search' => $baseDir . '/lib/request/search.php', - 'SearchProvider' => $baseDir . '/lib/default/searchprovider.php', - 'SendMail' => $baseDir . '/lib/request/sendmail.php', - 'Settings' => $baseDir . '/lib/request/settings.php', - 'SimpleMutex' => $baseDir . '/lib/default/simplemutex.php', - 'SqlStateMachine' => $baseDir . '/lib/default/sqlstatemachine.php', - 'StateInvalidException' => $baseDir . '/lib/exceptions/stateinvalidexception.php', - 'StateManager' => $baseDir . '/lib/core/statemanager.php', - 'StateNotFoundException' => $baseDir . '/lib/exceptions/statenotfoundexception.php', - 'StateNotYetAvailableException' => $baseDir . '/lib/exceptions/statenotyetavailableexception.php', - 'StateObject' => $baseDir . '/lib/core/stateobject.php', - 'StatusException' => $baseDir . '/lib/exceptions/statusexception.php', - 'Streamer' => $baseDir . '/lib/core/streamer.php', - 'StringStreamWrapper' => $baseDir . '/lib/utils/stringstreamwrapper.php', - 'Sync' => $baseDir . '/lib/request/sync.php', - 'SyncAppointment' => $baseDir . '/lib/syncobjects/syncappointment.php', - 'SyncAppointmentException' => $baseDir . '/lib/syncobjects/syncappointmentexception.php', - 'SyncAttachment' => $baseDir . '/lib/syncobjects/syncattachment.php', - 'SyncAttendee' => $baseDir . '/lib/syncobjects/syncattendee.php', - 'SyncBaseAttachment' => $baseDir . '/lib/syncobjects/syncbaseattachment.php', - 'SyncBaseBody' => $baseDir . '/lib/syncobjects/syncbasebody.php', - 'SyncCollections' => $baseDir . '/lib/core/synccollections.php', - 'SyncContact' => $baseDir . '/lib/syncobjects/synccontact.php', - 'SyncDeviceInformation' => $baseDir . '/lib/syncobjects/syncdeviceinformation.php', - 'SyncDevicePassword' => $baseDir . '/lib/syncobjects/syncdevicepassword.php', - 'SyncFolder' => $baseDir . '/lib/syncobjects/syncfolder.php', - 'SyncItemOperationsAttachment' => $baseDir . '/lib/syncobjects/syncitemoperationsattachment.php', - 'SyncMail' => $baseDir . '/lib/syncobjects/syncmail.php', - 'SyncMailFlags' => $baseDir . '/lib/syncobjects/syncmailflags.php', - 'SyncMeetingRequest' => $baseDir . '/lib/syncobjects/syncmeetingrequest.php', - 'SyncMeetingRequestRecurrence' => $baseDir . '/lib/syncobjects/syncmeetingrequestrecurrence.php', - 'SyncNote' => $baseDir . '/lib/syncobjects/syncnote.php', - 'SyncOOF' => $baseDir . '/lib/syncobjects/syncoof.php', - 'SyncOOFMessage' => $baseDir . '/lib/syncobjects/syncoofmessage.php', - 'SyncObject' => $baseDir . '/lib/syncobjects/syncobject.php', - 'SyncObjectBrokenException' => $baseDir . '/lib/exceptions/syncobjectbrokenexception.php', - 'SyncParameters' => $baseDir . '/lib/core/syncparameters.php', - 'SyncProvisioning' => $baseDir . '/lib/syncobjects/syncprovisioning.php', - 'SyncRRAvailability' => $baseDir . '/lib/syncobjects/syncresolverecipientsavailability.php', - 'SyncRRCertificates' => $baseDir . '/lib/syncobjects/syncresolverecipientscertificates.php', - 'SyncRROptions' => $baseDir . '/lib/syncobjects/syncresolverecipientsoptions.php', - 'SyncRRPicture' => $baseDir . '/lib/syncobjects/syncresolverecipientspicture.php', - 'SyncRecurrence' => $baseDir . '/lib/syncobjects/syncrecurrence.php', - 'SyncResolveRecipient' => $baseDir . '/lib/syncobjects/syncresolverecipient.php', - 'SyncResolveRecipients' => $baseDir . '/lib/syncobjects/syncresolverecipients.php', - 'SyncSendMail' => $baseDir . '/lib/syncobjects/syncsendmail.php', - 'SyncSendMailSource' => $baseDir . '/lib/syncobjects/syncsendmailsource.php', - 'SyncTask' => $baseDir . '/lib/syncobjects/synctask.php', - 'SyncTaskRecurrence' => $baseDir . '/lib/syncobjects/synctaskrecurrence.php', - 'SyncUserInformation' => $baseDir . '/lib/syncobjects/syncuserinformation.php', - 'SyncValidateCert' => $baseDir . '/lib/syncobjects/syncvalidatecert.php', - 'TimezoneUtil' => $baseDir . '/lib/utils/timezoneutil.php', - 'TopCollector' => $baseDir . '/lib/ipc/shm/topcollector.php', - 'TopCollectorRedis' => $baseDir . '/lib/ipc/redis/TopCollectorRedis.php', - 'Utils' => $baseDir . '/lib/utils/utils.php', - 'ValidateCert' => $baseDir . '/lib/request/validatecert.php', - 'WBXMLDecoder' => $baseDir . '/lib/wbxml/wbxmldecoder.php', - 'WBXMLDefs' => $baseDir . '/lib/wbxml/wbxmldefs.php', - 'WBXMLEncoder' => $baseDir . '/lib/wbxml/wbxmlencoder.php', - 'WBXMLException' => $baseDir . '/lib/exceptions/wbxmlexception.php', - 'Webservice' => $baseDir . '/lib/webservice/webservice.php', - 'WebserviceDevice' => $baseDir . '/lib/webservice/webservicedevice.php', - 'WebserviceUsers' => $baseDir . '/lib/webservice/webserviceusers.php', - 'ZLog' => $baseDir . '/lib/core/zlog.php', - 'ZPush' => $baseDir . '/lib/core/zpush.php', - 'ZPushAdmin' => $baseDir . '/lib/utils/zpushadmin.php', - 'ZPushAutodiscover' => $baseDir . '/autodiscover/autodiscover.php', - 'ZPushException' => $baseDir . '/lib/exceptions/zpushexception.php', - 'ZSyslog' => $baseDir . '/include/z_syslog.php', - 'carddav_backend' => $baseDir . '/include/z_carddav.php', - 'iCalComponent' => $baseDir . '/include/iCalendar.php', - 'iCalProp' => $baseDir . '/include/iCalendar.php', - 'iCalendar' => $baseDir . '/include/iCalendar.php', - 'rtf' => $baseDir . '/include/z_RTF.php', -); diff --git a/sources/vendor/composer/autoload_files.php b/sources/vendor/composer/autoload_files.php deleted file mode 100644 index 0017dfe..0000000 --- a/sources/vendor/composer/autoload_files.php +++ /dev/null @@ -1,13 +0,0 @@ - $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - - $loader->register(true); - - $includeFiles = require __DIR__ . '/autoload_files.php'; - foreach ($includeFiles as $file) { - composerRequire690cc3270d214f53d3ddd43de3041343($file); - } - - return $loader; - } -} - -function composerRequire690cc3270d214f53d3ddd43de3041343($file) -{ - require $file; -} diff --git a/sources/version.php b/sources/version.php deleted file mode 100644 index 485994b..0000000 --- a/sources/version.php +++ /dev/null @@ -1,45 +0,0 @@ -. -* -* Consult LICENSE file for details -************************************************/ - - -define("ZPUSH_VERSION", "SVN-trunk-r1981"); diff --git a/sources/z-push-admin.php b/sources/z-push-admin.php deleted file mode 100755 index 597aca9..0000000 --- a/sources/z-push-admin.php +++ /dev/null @@ -1,900 +0,0 @@ -#!/usr/bin/php -. -* -* Consult LICENSE file for details -************************************************/ - -require_once 'vendor/autoload.php'; -require_once 'config.php'; - -/** - * //TODO resync of single folders of a users device - */ - -/************************************************ - * MAIN - */ - define('BASE_PATH_CLI', dirname(__FILE__) ."/"); - set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI); - try { - ZPush::CheckConfig(); - ZLog::Initialize(); - ZPushAdminCLI::CheckEnv(); - ZPushAdminCLI::CheckOptions(); - - if (! ZPushAdminCLI::SureWhatToDo()) { - // show error message if available - if (ZPushAdminCLI::GetErrorMessage()) - echo "ERROR: ". ZPushAdminCLI::GetErrorMessage() . "\n"; - - echo ZPushAdminCLI::UsageInstructions(); - exit(1); - } - - ZPushAdminCLI::RunCommand(); - } - catch (ZPushException $zpe) { - die(get_class($zpe) . ": ". $zpe->getMessage() . "\n"); - } - - -/************************************************ - * Z-Push-Admin CLI - */ -class ZPushAdminCLI { - const COMMAND_SHOWALLDEVICES = 1; - const COMMAND_SHOWDEVICESOFUSER = 2; - const COMMAND_SHOWUSERSOFDEVICE = 3; - const COMMAND_WIPEDEVICE = 4; - const COMMAND_REMOVEDEVICE = 5; - const COMMAND_RESYNCDEVICE = 6; - const COMMAND_CLEARLOOP = 7; - const COMMAND_SHOWLASTSYNC = 8; - const COMMAND_RESYNCFOLDER = 9; - const COMMAND_FIXSTATES = 10; - const COMMAND_MAP = 11; - const COMMAND_UNMAP = 12; - - const TYPE_OPTION_EMAIL = "email"; - const TYPE_OPTION_CALENDAR = "calendar"; - const TYPE_OPTION_CONTACT = "contact"; - const TYPE_OPTION_TASK = "task"; - const TYPE_OPTION_NOTE = "note"; - - static private $command; - static private $user = false; - static private $device = false; - static private $type = false; - static private $backend = false; - static private $mappedUsername = false; - static private $errormessage; - - /** - * Returns usage instructions - * - * @return string - * @access public - */ - static public function UsageInstructions() { - $types = "'".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."' or '".self::TYPE_OPTION_NOTE."'"; - return "Usage:\n\tz-push-admin.php -a ACTION [options]\n\n" . - "Parameters:\n\t-a list/wipe/remove/resync/clearloop/fixstates/map/unmap\n" . - "\t[-u] username\n" . - "\t[-d] deviceid\n" . - "\t[-m] mappedUsername\n" . - "\t[-b] backend\n\n" . - "Actions:\n" . - "\tlist\t\t\t\t Lists all devices and synchronized users\n" . - "\tlist -u USER\t\t\t Lists all devices of user USER\n" . - "\tlist -d DEVICE\t\t\t Lists all users of device DEVICE\n" . - "\tlastsync\t\t\t Lists all devices and synchronized users and the last synchronization time\n" . - "\twipe -u USER\t\t\t Remote wipes all devices of user USER\n" . - "\twipe -d DEVICE\t\t\t Remote wipes device DEVICE\n" . - "\twipe -u USER -d DEVICE\t\t Remote wipes device DEVICE of user USER\n" . - "\tremove -u USER\t\t\t Removes all state data of all devices of user USER\n" . - "\tremove -d DEVICE\t\t Removes all state data of all users synchronized on device DEVICE\n" . - "\tremove -u USER -d DEVICE\t Removes all related state data of device DEVICE of user USER\n" . - "\tresync -u USER -d DEVICE\t Resynchronizes all data of device DEVICE of user USER\n" . - "\tresync -t TYPE \t\t\t Resynchronizes all folders of type $types for all devices and users.\n" . - "\tresync -t TYPE -u USER \t\t Resynchronizes all folders of type $types for the user USER.\n" . - "\tresync -t TYPE -u USER -d DEVICE Resynchronizes all folders of type $types for a specified device and user.\n" . - "\tresync -t FOLDERID -u USER\t Resynchronize the specified folder id only. The USER should be specified for better performance.\n" . - "\tclearloop\t\t\t Clears system wide loop detection data\n" . - "\tclearloop -d DEVICE -u USER\t Clears all loop detection data of a device DEVICE and an optional user USER\n" . - "\tfixstates\t\t\t Checks the states for integrity and fixes potential issues\n" . - "\tmap -u USER -b BACKEND -m USER2\t Maps USER for BACKEND to username USER2 (when using 'combined' backend)\n" . - "\tunmap -u USER -b BACKEND\t Removes the mapping for USER and BACKEND (when using 'combined' backend)\n" . - "\n"; - } - - /** - * Checks the environment - * - * @return - * @access public - */ - static public function CheckEnv() { - if (php_sapi_name() != "cli") - self::$errormessage = sprintf("This script should not be called in a browser. Called from: %s", php_sapi_name()); - - if (!function_exists("getopt")) - self::$errormessage = "PHP Function getopt not found. Please check your PHP version and settings."; - } - - /** - * Checks the options from the command line - * - * @return - * @access public - */ - static public function CheckOptions() { - if (self::$errormessage) - return; - - $options = getopt("u:d:a:t:b:m:"); - - // get 'user' - if (isset($options['u']) && !empty($options['u'])) - self::$user = strtolower(trim($options['u'])); - else if (isset($options['user']) && !empty($options['user'])) - self::$user = strtolower(trim($options['user'])); - - // get 'device' - if (isset($options['d']) && !empty($options['d'])) - self::$device = strtolower(trim($options['d'])); - else if (isset($options['device']) && !empty($options['device'])) - self::$device = strtolower(trim($options['device'])); - - // get 'action' - $action = false; - if (isset($options['a']) && !empty($options['a'])) - $action = strtolower(trim($options['a'])); - elseif (isset($options['action']) && !empty($options['action'])) - $action = strtolower(trim($options['action'])); - - // get 'type' - if (isset($options['t']) && !empty($options['t'])) - self::$type = strtolower(trim($options['t'])); - elseif (isset($options['type']) && !empty($options['type'])) - self::$type = strtolower(trim($options['type'])); - - // if type is set, it must be one of known types or a 44 byte long folder id - if (self::$type !== false) { - if (self::$type !== self::TYPE_OPTION_EMAIL && - self::$type !== self::TYPE_OPTION_CALENDAR && - self::$type !== self::TYPE_OPTION_CONTACT && - self::$type !== self::TYPE_OPTION_TASK && - self::$type !== self::TYPE_OPTION_NOTE && - strlen(self::$type) !== 44) { - self::$errormessage = "Wrong 'type'. Possible values are: ". - "'".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."', '".self::TYPE_OPTION_NOTE."' ". - "or a 44 byte long folder id (as hex)."; - return; - } - } - - // get 'backend' - if (isset($options['b']) && !empty($options['b'])) - self::$backend = strtolower(trim($options['b'])); - elseif (isset($options['backend']) && !empty($options['backend'])) - self::$backend = strtolower(trim($options['backend'])); - - // get 'map' - if (isset($options['m']) && !empty($options['m'])) - self::$mappedUsername = trim($options['m']); - elseif (isset($options['mappedUsername']) && !empty($options['mappedUsername'])) - self::$mappedUsername = trim($options['mappedUsername']); - - // get a command for the requested action - switch ($action) { - // list data - case "list": - if (self::$user === false && self::$device === false) - self::$command = self::COMMAND_SHOWALLDEVICES; - - if (self::$user !== false) - self::$command = self::COMMAND_SHOWDEVICESOFUSER; - - if (self::$device !== false) - self::$command = self::COMMAND_SHOWUSERSOFDEVICE; - break; - - // list data - case "lastsync": - self::$command = self::COMMAND_SHOWLASTSYNC; - break; - - // remove wipe device - case "wipe": - if (self::$user === false && self::$device === false) - self::$errormessage = "Not possible to execute remote wipe. Device, user or both must be specified."; - else - self::$command = self::COMMAND_WIPEDEVICE; - break; - - // remove device data of user - case "remove": - if (self::$user === false && self::$device === false) - self::$errormessage = "Not possible to remove data. Device, user or both must be specified."; - else - self::$command = self::COMMAND_REMOVEDEVICE; - break; - - // resync a device - case "resync": - case "re-sync": - case "sync": - case "resynchronize": - case "re-synchronize": - case "synchronize": - // full resync - if (self::$type === false) { - if (self::$user === false || self::$device === false) - self::$errormessage = "Not possible to resynchronize device. Device and user must be specified."; - else - self::$command = self::COMMAND_RESYNCDEVICE; - } - else { - self::$command = self::COMMAND_RESYNCFOLDER; - } - break; - - // clear loop detection data - case "clearloop": - case "clearloopdetection": - self::$command = self::COMMAND_CLEARLOOP; - break; - - // clear loop detection data - case "fixstates": - case "fix": - self::$command = self::COMMAND_FIXSTATES; - break; - - // map users for 'combined' backend - case "map": - if (self::$user === false || self::$backend === false || self::$mappedUsername === false) - self::$errormessage = "Not possible to map. User, backend and target user must be specified."; - else - self::$command = self::COMMAND_MAP; - break; - - // unmap users for 'combined' backend - case "unmap": - if (self::$user === false || self::$backend === false) - self::$errormessage = "Not possible to unmap. User and backend must be specified."; - else - self::$command = self::COMMAND_UNMAP; - break; - - default: - self::UsageInstructions(); - } - } - - /** - * Indicates if the options from the command line - * could be processed correctly - * - * @return boolean - * @access public - */ - static public function SureWhatToDo() { - return isset(self::$command); - } - - /** - * Returns a errormessage of things which could have gone wrong - * - * @return string - * @access public - */ - static public function GetErrorMessage() { - return (isset(self::$errormessage))?self::$errormessage:""; - } - - /** - * Runs a command requested from an action of the command line - * - * @return - * @access public - */ - static public function RunCommand() { - echo "\n"; - switch(self::$command) { - case self::COMMAND_SHOWALLDEVICES: - self::CommandShowDevices(); - break; - - case self::COMMAND_SHOWDEVICESOFUSER: - self::CommandShowDevices(); - break; - - case self::COMMAND_SHOWUSERSOFDEVICE: - self::CommandDeviceUsers(); - break; - - case self::COMMAND_SHOWLASTSYNC: - self::CommandShowLastSync(); - break; - - case self::COMMAND_WIPEDEVICE: - if (self::$device) - echo sprintf("Are you sure you want to REMOTE WIPE device '%s' [y/N]: ", self::$device); - else - echo sprintf("Are you sure you want to REMOTE WIPE all devices of user '%s' [y/N]: ", self::$user); - - $confirm = strtolower(trim(fgets(STDIN))); - if ( $confirm === 'y' || $confirm === 'yes') - self::CommandWipeDevice(); - else - echo "Aborted!\n"; - break; - - case self::COMMAND_REMOVEDEVICE: - self::CommandRemoveDevice(); - break; - - case self::COMMAND_RESYNCDEVICE: - if (self::$device == false) { - echo sprintf("Are you sure you want to re-synchronize all devices of user '%s' [y/N]: ", self::$user); - $confirm = strtolower(trim(fgets(STDIN))); - if ( !($confirm === 'y' || $confirm === 'yes')) { - echo "Aborted!\n"; - exit(1); - } - } - self::CommandResyncDevices(); - break; - - case self::COMMAND_RESYNCFOLDER: - if (self::$device == false && self::$user == false) { - echo "Are you sure you want to re-synchronize this folder type of all devices and users [y/N]: "; - $confirm = strtolower(trim(fgets(STDIN))); - if ( !($confirm === 'y' || $confirm === 'yes')) { - echo "Aborted!\n"; - exit(1); - } - } - self::CommandResyncFolder(); - break; - - case self::COMMAND_CLEARLOOP: - self::CommandClearLoopDetectionData(); - break; - - case self::COMMAND_FIXSTATES: - self::CommandFixStates(); - break; - - case self::COMMAND_MAP: - self::CommandMap(); - break; - - case self::COMMAND_UNMAP: - self::CommandUnmap(); - break; - } - echo "\n"; - } - - /** - * Command "Show all devices" and "Show devices of user" - * Prints the device id of/and connected users - * - * @return - * @access public - */ - static public function CommandShowDevices() { - $devicelist = ZPushAdmin::ListDevices(self::$user); - if (empty($devicelist)) - echo "\tno devices found\n"; - else { - if (self::$user === false) { - echo "All synchronized devices\n\n"; - echo str_pad("Device id", 36). "Synchronized users\n"; - echo "-----------------------------------------------------\n"; - } - else - echo "Synchronized devices of user: ". self::$user. "\n"; - } - - foreach ($devicelist as $deviceId) { - if (self::$user === false) { - echo str_pad($deviceId, 36) . implode (",", ZPushAdmin::ListUsers($deviceId)) ."\n"; - } - else - self::printDeviceData($deviceId, self::$user); - } - } - - /** - * Command "Show all devices and users with last sync time" - * Prints the device id of/and connected users - * - * @return - * @access public - */ - static public function CommandShowLastSync() { - $devicelist = ZPushAdmin::ListDevices(false); - if (empty($devicelist)) - echo "\tno devices found\n"; - else { - echo "All known devices and users and their last synchronization time\n\n"; - echo str_pad("Device id", 36). str_pad("Synchronized user", 31)."Last sync time\n"; - echo "-----------------------------------------------------------------------------------------------------\n"; - } - - foreach ($devicelist as $deviceId) { - $users = ZPushAdmin::ListUsers($deviceId); - foreach ($users as $user) { - $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); - echo str_pad($deviceId, 36) . str_pad($user, 30) . " " . ($device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) : "never") . "\n"; - } - } - } - - /** - * Command "Show users of device" - * Prints informations about all users which use a device - * - * @return - * @access public - */ - static public function CommandDeviceUsers() { - $users = ZPushAdmin::ListUsers(self::$device); - - if (empty($users)) { - echo "\tno user data synchronized to device\n"; - } - // if a user is specified, we only want to see the devices of this one - else if (self::$user !== false && !in_array(self::$user, $users)) { - printf("\tuser '%s' not known in device data '%s'\n", self::$user, self::$device); - } - - foreach ($users as $user) { - if (self::$user !== false && strtolower($user) !== self::$user) { - continue; - } - echo "Synchronized by user: ". $user. "\n"; - self::printDeviceData(self::$device, $user); - } - } - - /** - * Command "Wipe device" - * Marks a device of that user to be remotely wiped - * - * @return - * @access public - */ - static public function CommandWipeDevice() { - $stat = ZPushAdmin::WipeDevice($_SERVER["LOGNAME"], self::$user, self::$device); - - if (self::$user !== false && self::$device !== false) { - echo sprintf("Mark device '%s' of user '%s' to be wiped: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - - if ($stat) { - echo "Updated information about this device:\n"; - self::printDeviceData(self::$device, self::$user); - } - } - elseif (self::$user !== false) { - echo sprintf("Mark devices of user '%s' to be wiped: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - self::CommandShowDevices(); - } - } - - /** - * Command "Remove device" - * Remove a device of that user from the device list - * - * @return - * @access public - */ - static public function CommandRemoveDevice() { - $stat = ZPushAdmin::RemoveDevice(self::$user, self::$device); - if (self::$user === false) - echo sprintf("State data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - elseif (self::$device === false) - echo sprintf("State data of all devices of user '%s' removed: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - else - echo sprintf("State data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - } - - /** - * Command "Resync device(s)" - * Resyncs one or all devices of that user - * - * @return - * @access public - */ - static public function CommandResyncDevices() { - $stat = ZPushAdmin::ResyncDevice(self::$user, self::$device); - echo sprintf("Resync of device '%s' of user '%s': %s", self::$device, self::$user, ($stat)?'Requested':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - } - - /** - * Command "Resync folder(s)" - * Resyncs a folder type of a specific device/user or of all users - * - * @return - * @access public - */ - static public function CommandResyncFolder() { - // if no device is specified, search for all devices of a user. If user is not set, all devices are returned. - if (self::$device === false) { - $devicelist = ZPushAdmin::ListDevices(self::$user); - if (empty($devicelist)) { - echo "\tno devices/users found\n"; - return true; - } - } - else - $devicelist = array(self::$device); - - foreach ($devicelist as $deviceId) { - $users = ZPushAdmin::ListUsers($deviceId); - foreach ($users as $user) { - if (self::$user && self::$user != $user) - continue; - self::resyncFolder($deviceId, $user, self::$type); - } - } - - } - - /** - * Command to clear the loop detection data - * Mobiles may enter loop detection (one-by-one synchring due to timeouts / erros). - * - * @return - * @access public - */ - static public function CommandClearLoopDetectionData() { - $stat = false; - $stat = ZPushAdmin::ClearLoopDetectionData(self::$user, self::$device); - if (self::$user === false && self::$device === false) - echo sprintf("System wide loop detection data removed: %s", ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - elseif (self::$user === false) - echo sprintf("Loop detection data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - elseif (self::$device === false && self::$user !== false) - echo sprintf("Error: %s", ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_WARN)). "\n"; - else - echo sprintf("Loop detection data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; - } - - /** - * Resynchronizes a folder type of a device & user - * - * @param string $deviceId the id of the device - * @param string $user the user - * @param string $type the folder type - * - * @return - * @access private - */ - static private function resyncFolder($deviceId, $user, $type) { - $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); - - if (! $device instanceof ASDevice) { - echo sprintf("Folder resync failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); - return false; - } - - $folders = array(); - foreach ($device->GetAllFolderIds() as $folderid) { - // if submitting a folderid as type to resync a specific folder. - if ($folderid == $type) { - printf("Found and resynching requested folderid '%s' on device '%s' of user '%s'\n", $folderid, $deviceId, $user); - $folders[] = $folderid; - break; - } - - if ($device->GetFolderUUID($folderid)) { - $foldertype = $device->GetFolderType($folderid); - switch($foldertype) { - case SYNC_FOLDER_TYPE_APPOINTMENT: - case SYNC_FOLDER_TYPE_USER_APPOINTMENT: - if ($type == "calendar") - $folders[] = $folderid; - break; - case SYNC_FOLDER_TYPE_CONTACT: - case SYNC_FOLDER_TYPE_USER_CONTACT: - if ($type == "contact") - $folders[] = $folderid; - break; - case SYNC_FOLDER_TYPE_TASK: - case SYNC_FOLDER_TYPE_USER_TASK: - if ($type == "task") - $folders[] = $folderid; - break; - case SYNC_FOLDER_TYPE_NOTE: - case SYNC_FOLDER_TYPE_USER_NOTE: - if ($type == "note") - $folders[] = $folderid; - break; - default: - if ($type == "email") - $folders[] = $folderid; - break; - } - } - } - - $stat = ZPushAdmin::ResyncFolder($user, $deviceId, $folders); - echo sprintf("Resync of %d folders of type %s on device '%s' of user '%s': %s\n", count($folders), $type, $deviceId, $user, ($stat)?'Requested':ZLog::GetLastMessage(LOGLEVEL_ERROR)); - } - - /** - * Fixes the states for potential issues - * - * @return - * @access private - */ - static private function CommandFixStates() { - echo "Validating and fixing states (this can take some time):\n"; - - echo "\tChecking devicedata states: "; - if ($stat = ZPushAdmin::FixStatesWrongDevicedata()) - printf("Devices: fixed %d - ok %d Users: removed %d - ok %d\n",$stat[0], $stat[1], $stat[2], $stat[3]); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - - echo "\tChecking username casings: "; - if ($stat = ZPushAdmin::FixStatesDifferentUsernameCases()) - printf("Processed: %d - Converted: %d - Removed: %d\n", $stat[0], $stat[1], $stat[2]); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - - // fixes ZP-339 - echo "\tChecking available devicedata & user linking: "; - if ($stat = ZPushAdmin::FixStatesDeviceToUserLinking()) - printf("Unlinked: %d - Linked: %d\n", $stat[0], $stat[1]); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - - echo "\tChecking for unreferenced (obsolete) state files: "; - if (($stat = ZPushAdmin::FixStatesUserToStatesLinking()) !== false) - printf("Processed: %d - Deleted: %d\n", $stat[0], $stat[1]); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - } - - /** - * Maps a username for a specific backend to another username - * - * @return - * @access private - */ - static private function CommandMap() { - if (ZPushAdmin::AddUsernameMapping(self::$user, self::$backend, self::$mappedUsername)) - printf("Successfully mapped username.\n"); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - } - - /** - * Deletes the mapping for a username and a specific bakkend - * - * @return - * @access private - */ - static private function CommandUnmap() { - if (ZPushAdmin::RemoveUsernameMapping(self::$user, self::$backend)) - printf("Successfully unmapped username.\n"); - else - echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; - } - - /** - * Prints detailed informations about a device - * - * @param string $deviceId the id of the device - * @param string $user the user - * - * @return - * @access private - */ - static private function printDeviceData($deviceId, $user) { - $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); - - if (! $device instanceof ASDevice) { - echo sprintf("Folder resync failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); - return false; - } - - // Gather some statistics about synchronized folders - $folders = $device->GetAllFolderIds(); - $synchedFolders = 0; - $synchedFolderTypes = array(); - $syncedFoldersInProgress = 0; - foreach ($folders as $folderid) { - if ($device->GetFolderUUID($folderid)) { - $synchedFolders++; - $type = $device->GetFolderType($folderid); - switch($type) { - case SYNC_FOLDER_TYPE_APPOINTMENT: - case SYNC_FOLDER_TYPE_USER_APPOINTMENT: - $gentype = "Calendars"; - break; - case SYNC_FOLDER_TYPE_CONTACT: - case SYNC_FOLDER_TYPE_USER_CONTACT: - $gentype = "Contacts"; - break; - case SYNC_FOLDER_TYPE_TASK: - case SYNC_FOLDER_TYPE_USER_TASK: - $gentype = "Tasks"; - break; - case SYNC_FOLDER_TYPE_NOTE: - case SYNC_FOLDER_TYPE_USER_NOTE: - $gentype = "Notes"; - break; - default: - $gentype = "Emails"; - break; - } - if (!isset($synchedFolderTypes[$gentype])) - $synchedFolderTypes[$gentype] = 0; - $synchedFolderTypes[$gentype]++; - - // set the folder name for all folders which are not fully synchronized yet - $fstatus = $device->GetFolderSyncStatus($folderid); - if ($fstatus !== false && is_array($fstatus)) { - // TODO would be nice if we could see the real name of the folder, right now we use the folder type as name - $fstatus['name'] = $gentype; - $device->SetFolderSyncStatus($folderid, $fstatus); - $syncedFoldersInProgress++; - } - } - } - $folderinfo = ""; - foreach ($synchedFolderTypes as $gentype=>$count) { - $folderinfo .= $gentype; - if ($count>1) $folderinfo .= "($count)"; - $folderinfo .= " "; - } - if (!$folderinfo) $folderinfo = "None available"; - - echo "-----------------------------------------------------\n"; - echo "DeviceId:\t\t$deviceId\n"; - echo "Device type:\t\t". ($device->GetDeviceType() !== ASDevice::UNDEFINED ? $device->GetDeviceType() : "unknown") ."\n"; - echo "UserAgent:\t\t".($device->GetDeviceUserAgent()!== ASDevice::UNDEFINED ? $device->GetDeviceUserAgent() : "unknown") ."\n"; - // TODO implement $device->GetDeviceUserAgentHistory() - - // device information transmitted during Settings command - if ($device->GetDeviceModel()) - echo "Device Model:\t\t". $device->GetDeviceModel(). "\n"; - if ($device->GetDeviceIMEI()) - echo "Device IMEI:\t\t". $device->GetDeviceIMEI(). "\n"; - if ($device->GetDeviceFriendlyName()) - echo "Device friendly name:\t". $device->GetDeviceFriendlyName(). "\n"; - if ($device->GetDeviceOS()) - echo "Device OS:\t\t". $device->GetDeviceOS(). "\n"; - if ($device->GetDeviceOSLanguage()) - echo "Device OS Language:\t". $device->GetDeviceOSLanguage(). "\n"; - if ($device->GetDevicePhoneNumber()) - echo "Device Phone nr:\t". $device->GetDevicePhoneNumber(). "\n"; - if ($device->GetDeviceMobileOperator()) - echo "Device Operator:\t". $device->GetDeviceMobileOperator(). "\n"; - if ($device->GetDeviceEnableOutboundSMS()) - echo "Device Outbound SMS:\t". $device->GetDeviceEnableOutboundSMS(). "\n"; - - echo "ActiveSync version:\t".($device->GetASVersion() ? $device->GetASVersion() : "unknown") ."\n"; - echo "First sync:\t\t". strftime("%Y-%m-%d %H:%M", $device->GetFirstSyncTime()) ."\n"; - echo "Last sync:\t\t". ($device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) : "never")."\n"; - echo "Total folders:\t\t". count($folders). "\n"; - echo "Synchronized folders:\t". $synchedFolders; - if ($syncedFoldersInProgress > 0) - echo " (". $syncedFoldersInProgress. " in progress)"; - echo "\n"; - echo "Synchronized data:\t$folderinfo\n"; - if ($syncedFoldersInProgress > 0) { - echo "Synchronization progress:\n"; - foreach ($folders as $folderid) { - $d = $device->GetFolderSyncStatus($folderid); - if ($d) { - $status = ""; - if ($d['total'] > 0) { - $percent = round($d['done']*100/$d['total']); - $status = sprintf("Status: %s%d%% (%d/%d)", ($percent < 10)?" ":"", $percent, $d['done'], $d['total']); - } - printf("\tFolder: %s%s Sync: %s %s\n", $d['name'], str_repeat(" ", 12-strlen($d['name'])), $d['status'], $status); - } - } - } - echo "Status:\t\t\t"; - switch ($device->GetWipeStatus()) { - case SYNC_PROVISION_RWSTATUS_OK: - echo "OK\n"; - break; - case SYNC_PROVISION_RWSTATUS_PENDING: - echo "Pending wipe\n"; - break; - case SYNC_PROVISION_RWSTATUS_REQUESTED: - echo "Wipe requested on device\n"; - break; - case SYNC_PROVISION_RWSTATUS_WIPED: - echo "Wiped\n"; - break; - default: - echo "Not available\n"; - break; - } - - echo "WipeRequest on:\t\t". ($device->GetWipeRequestedOn() ? strftime("%Y-%m-%d %H:%M", $device->GetWipeRequestedOn()) : "not set")."\n"; - echo "WipeRequest by:\t\t". ($device->GetWipeRequestedBy() ? $device->GetWipeRequestedBy() : "not set")."\n"; - echo "Wiped on:\t\t". ($device->GetWipeActionOn() ? strftime("%Y-%m-%d %H:%M", $device->GetWipeActionOn()) : "not set")."\n"; - - echo "Attention needed:\t"; - - if ($device->GetDeviceError()) - echo $device->GetDeviceError() ."\n"; - else if (!isset($device->ignoredmessages) || empty($device->ignoredmessages)) { - echo "No errors known\n"; - } - else { - printf("%d messages need attention because they could not be synchronized\n", count($device->ignoredmessages)); - foreach ($device->ignoredmessages as $im) { - $info = ""; - if (isset($im->asobject->subject)) - $info .= sprintf("Subject: '%s'", $im->asobject->subject); - if (isset($im->asobject->fileas)) - $info .= sprintf("FileAs: '%s'", $im->asobject->fileas); - if (isset($im->asobject->from)) - $info .= sprintf(" - From: '%s'", $im->asobject->from); - if (isset($im->asobject->starttime)) - $info .= sprintf(" - On: '%s'", strftime("%Y-%m-%d %H:%M", $im->asobject->starttime)); - $reason = $im->reasonstring; - if ($im->reasoncode == 2) - $reason = "Message was causing loop"; - printf("\tBroken object:\t'%s' ignored on '%s'\n", $im->asclass, strftime("%Y-%m-%d %H:%M", $im->timestamp)); - printf("\tInformation:\t%s\n", $info); - printf("\tReason: \t%s (%s)\n", $reason, $im->reasoncode); - printf("\tItem/Parent id: %s/%s\n", $im->id, $im->folderid); - echo "\n"; - } - } - - } -} diff --git a/sources/z-push-top.php b/sources/z-push-top.php deleted file mode 100755 index 32dafe0..0000000 --- a/sources/z-push-top.php +++ /dev/null @@ -1,761 +0,0 @@ -#!/usr/bin/php -. -* -* Consult LICENSE file for details -************************************************/ - -require_once 'vendor/autoload.php'; -require_once 'config.php'; - -/************************************************ - * MAIN - */ - declare(ticks = 1); - define('BASE_PATH_CLI', dirname(__FILE__) ."/"); - - try { - ZPush::CheckConfig(); - ZLog::Initialize(); - if (!function_exists("pcntl_signal")) - throw new FatalException("Function pcntl_signal() is not available. Please install package 'php5-pcntl' (or similar) on your system."); - - $zpt = new ZPushTop(); - - // check if help was requested from CLI - if (in_array('-h', $argv) || in_array('--help', $argv)) { - echo $zpt->UsageInstructions(); - exit(1); - } - - if ($zpt->IsAvailable()) { - pcntl_signal(SIGINT, array($zpt, "SignalHandler")); - $zpt->run(); - $zpt->scrClear(); - system("stty sane"); - } - else - echo "Z-Push shared memory interprocess communication is not available.\n"; - } - catch (ZPushException $zpe) { - die(get_class($zpe) . ": ". $zpe->getMessage() . "\n"); - } - - echo "terminated\n"; - - -/************************************************ - * Z-Push-Top - */ -class ZPushTop { - // show options - const SHOW_DEFAULT = 0; - const SHOW_ACTIVE_ONLY = 1; - const SHOW_UNKNOWN_ONLY = 2; - const SHOW_TERM_DEFAULT_TIME = 5; // 5 secs - - private $topCollector; - private $starttime; - private $action; - private $filter; - private $status; - private $statusexpire; - private $wide; - private $wasEnabled; - private $terminate; - private $scrSize; - private $pingInterval; - private $showPush; - private $showTermSec; - - private $linesActive = array(); - private $linesOpen = array(); - private $linesUnknown = array(); - private $linesTerm = array(); - private $pushConn = 0; - private $activeConn = array(); - private $activeHosts = array(); - private $activeUsers = array(); - private $activeDevices = array(); - - /** - * Constructor - * - * @access public - */ - public function ZPushTop() { - $this->starttime = time(); - $this->currenttime = time(); - $this->action = ""; - $this->filter = false; - $this->status = false; - $this->statusexpire = 0; - $this->helpexpire = 0; - $this->doingTail = false; - $this->wide = false; - $this->terminate = false; - $this->showPush = true; - $this->showOption = self::SHOW_DEFAULT; - $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME; - $this->scrSize = array('width' => 80, 'height' => 24); - $this->pingInterval = (defined('PING_INTERVAL') && PING_INTERVAL > 0) ? PING_INTERVAL : 12; - - // get a TopCollector - $this->topCollector = ZPush::GetTopCollector(); - } - - /** - * Requests data from the running Z-Push processes - * - * @access private - * @return - */ - private function initialize() { - // request feedback from active processes - $this->wasEnabled = $this->topCollector->CollectData(); - - // remove obsolete data - $this->topCollector->ClearLatest(true); - - // start with default colours - $this->scrDefaultColors(); - } - - /** - * Main loop of Z-Push-top - * Runs until termination is requested - * - * @access public - * @return - */ - public function run() { - $this->initialize(); - - // Non-blocking read from stdin - stream_set_blocking(STDIN, FALSE); - - do { - $this->currenttime = time(); - - // see if shared memory is active - if (!$this->IsAvailable()) - $this->terminate = true; - - // active processes should continue sending data - $this->topCollector->CollectData(); - - // get and process data from processes - $this->topCollector->ClearLatest(); - $topdata = $this->topCollector->ReadLatest(); - $this->processData($topdata); - - // clear screen - $this->scrClear(); - - // check if screen size changed - $s = $this->scrGetSize(); - if ($this->scrSize['width'] != $s['width']) { - if ($s['width'] > 180) - $this->wide = true; - else - $this->wide = false; - } - $this->scrSize = $s; - - // print overview - $this->scrOverview(); - - // wait for user input - $this->readLineProcess(); - } - while($this->terminate != true); - } - - /** - * Indicates if TopCollector is available collecting data - * - * @access public - * @return boolean - */ - public function IsAvailable() { - return $this->topCollector->IsActive(); - } - - /** - * Processes data written by the running processes - * - * @param array $data - * - * @access private - * @return - */ - private function processData($data) { - $this->linesActive = array(); - $this->linesOpen = array(); - $this->linesUnknown = array(); - $this->linesTerm = array(); - $this->pushConn = 0; - $this->activeConn = array(); - $this->activeHosts = array(); - $this->activeUsers = array(); - $this->activeDevices = array(); - - if (!is_array($data)) - return; - - foreach ($data as $devid=>$users) { - foreach ($users as $user=>$pids) { - foreach ($pids as $pid=>$line) { - if (!is_array($line)) - continue; - - $line['command'] = Utils::GetCommandFromCode($line['command']); - - if ($line["ended"] == 0) { - $this->activeDevices[$devid] = 1; - $this->activeUsers[$user] = 1; - $this->activeConn[$pid] = 1; - $this->activeHosts[$line['ip']] = 1; - - $line["time"] = $this->currenttime - $line['start']; - if ($line['push'] === true) $this->pushConn += 1; - - // ignore push connections - if ($line['push'] === true && ! $this->showPush) - continue; - - if ($this->filter !== false) { - $f = $this->filter; - if (!($line["pid"] == $f || $line["ip"] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) || - preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) )) - continue; - } - - $lastUpdate = $this->currenttime - $line["update"]; - if ($this->currenttime - $line["update"] < 2) - $this->linesActive[$line["update"].$line["pid"]] = $line; - else if (($line['push'] === true && $lastUpdate > ($this->pingInterval+2)) || ($line['push'] !== true && $lastUpdate > 4)) - $this->linesUnknown[$line["update"].$line["pid"]] = $line; - else - $this->linesOpen[$line["update"].$line["pid"]] = $line; - } - else { - // do not show terminated + expired connections - if ($line['ended'] + $this->showTermSec < $this->currenttime) - continue; - - if ($this->filter !== false) { - $f = $this->filter; - if (!($line['pid'] == $f || $line['ip'] == $f || strtolower($line['command']) == strtolower($f) || preg_match("/.*?$f.*?/i", $line['user']) || - preg_match("/.*?$f.*?/i", $line['devagent']) || preg_match("/.*?$f.*?/i", $line['devid']) || preg_match("/.*?$f.*?/i", $line['addinfo']) )) - continue; - } - - $line['time'] = $line['ended'] - $line['start']; - $this->linesTerm[$line['update'].$line['pid']] = $line; - } - } - } - } - - // sort by execution time - krsort($this->linesActive); - krsort($this->linesOpen); - krsort($this->linesUnknown); - krsort($this->linesTerm); - } - - /** - * Prints data to the terminal - * - * @access private - * @return - */ - private function scrOverview() { - $linesAvail = $this->scrSize['height'] - 8; - $lc = 1; - $this->scrPrintAt($lc,0, "\033[1mZ-Push top live statistics\033[0m\t\t\t\t\t". @strftime("%d/%m/%Y %T")."\n"); $lc++; - - $this->scrPrintAt($lc,0, sprintf("Open connections: %d\t\t\t\tUsers:\t %d\tZ-Push: %s ",count($this->activeConn),count($this->activeUsers), $this->getVersion())); $lc++; - $this->scrPrintAt($lc,0, sprintf("Push connections: %d\t\t\t\tDevices: %d\tPHP-MAPI: %s", $this->pushConn, count($this->activeDevices),phpversion("mapi"))); $lc++; - $this->scrPrintAt($lc,0, sprintf(" Hosts:\t %d", count($this->activeHosts))); $lc++; - $lc++; - - $this->scrPrintAt($lc,0, "\033[4m". $this->getLine(array('pid'=>'PID', 'ip'=>'IP', 'user'=>'USER', 'command'=>'COMMAND', 'time'=>'TIME', 'devagent'=>'AGENT', 'devid'=>'DEVID', 'addinfo'=>'Additional Information')). str_repeat(" ",20)."\033[0m"); $lc++; - - // print help text if requested - $hl = 0; - if ($this->helpexpire > $this->currenttime) { - $help = $this->scrHelp(); - $linesAvail -= count($help); - $hl = $this->scrSize['height'] - count($help) -1; - foreach ($help as $h) { - $this->scrPrintAt($hl,0, $h); - $hl++; - } - } - - $toPrintActive = $linesAvail; - $toPrintOpen = $linesAvail; - $toPrintUnknown = $linesAvail; - $toPrintTerm = $linesAvail; - - // default view: show all unknown, no terminated and half active+open - if (count($this->linesActive) + count($this->linesOpen) + count($this->linesUnknown) > $linesAvail) { - $toPrintUnknown = count($this->linesUnknown); - $toPrintActive = count($this->linesActive); - $toPrintOpen = $linesAvail-$toPrintUnknown-$toPrintActive; - $toPrintTerm = 0; - } - - if ($this->showOption == self::SHOW_ACTIVE_ONLY) { - $toPrintActive = $linesAvail; - $toPrintOpen = 0; - $toPrintUnknown = 0; - $toPrintTerm = 0; - } - - if ($this->showOption == self::SHOW_UNKNOWN_ONLY) { - $toPrintActive = 0; - $toPrintOpen = 0; - $toPrintUnknown = $linesAvail; - $toPrintTerm = 0; - } - - $linesprinted = 0; - foreach ($this->linesActive as $time=>$l) { - if ($linesprinted >= $toPrintActive) - break; - - $this->scrPrintAt($lc,0, "\033[01m" . $this->getLine($l) ."\033[0m"); - $lc++; - $linesprinted++; - } - - $linesprinted = 0; - foreach ($this->linesOpen as $time=>$l) { - if ($linesprinted >= $toPrintOpen) - break; - - $this->scrPrintAt($lc,0, $this->getLine($l)); - $lc++; - $linesprinted++; - } - - $linesprinted = 0; - foreach ($this->linesUnknown as $time=>$l) { - if ($linesprinted >= $toPrintUnknown) - break; - - $color = "0;31m"; - if ($l['push'] == false && $time - $l["start"] > 30) - $color = "1;31m"; - $this->scrPrintAt($lc,0, "\033[0". $color . $this->getLine($l) ."\033[0m"); - $lc++; - $linesprinted++; - } - - if ($toPrintTerm > 0) - $toPrintTerm = $linesAvail - $lc +6; - - $linesprinted = 0; - foreach ($this->linesTerm as $time=>$l){ - if ($linesprinted >= $toPrintTerm) - break; - - $this->scrPrintAt($lc,0, "\033[01;30m" . $this->getLine($l) ."\033[0m"); - $lc++; - $linesprinted++; - } - - // add the lines used when displaying the help text - $lc += $hl; - $this->scrPrintAt($lc,0, "\033[K"); $lc++; - $this->scrPrintAt($lc,0, "Colorscheme: \033[01mActive \033[0mOpen \033[01;31mUnknown \033[01;30mTerminated\033[0m"); - - // remove old status - if ($this->statusexpire < $this->currenttime) - $this->status = false; - - // show request information and help command - if ($this->starttime + 6 > $this->currenttime) { - $this->status = sprintf("Requesting information (takes up to %dsecs)", $this->pingInterval). str_repeat(".", ($this->currenttime-$this->starttime)) . " type \033[01;31mh\033[00;31m or \033[01;31mhelp\033[00;31m for usage instructions"; - $this->statusexpire = $this->currenttime+1; - } - - - $str = ""; - if (! $this->showPush) - $str .= "\033[00;32mPush: \033[01;32mNo\033[0m "; - - if ($this->showOption == self::SHOW_ACTIVE_ONLY) - $str .= "\033[01;32mActive only\033[0m "; - - if ($this->showOption == self::SHOW_UNKNOWN_ONLY) - $str .= "\033[01;32mUnknown only\033[0m "; - - if ($this->showTermSec != self::SHOW_TERM_DEFAULT_TIME) - $str .= "\033[01;32mTerminated: ". $this->showTermSec. "s\033[0m "; - - if ($this->filter !== false || ($this->status !== false && $this->statusexpire > $this->currenttime)) { - // print filter in green - if ($this->filter !== false) - $str .= "\033[00;32mFilter: \033[01;32m$this->filter\033[0m "; - // print status in red - if ($this->status !== false) - $str .= "\033[00;31m$this->status\033[0m"; - } - $this->scrPrintAt(5,0, $str); - - $this->scrPrintAt(4,0,"Action: \033[01m" . $this->action . "\033[0m"); - } - - /** - * Waits for a keystroke and processes the requested command - * - * @access private - * @return - */ - private function readLineProcess() { - //$ans = explode("^^", shell_exec('bash -c "read -n 1 -t 1 ANS ; echo \\\$?^^\\\$ANS;"')); - sleep(1); - $ans = false; - $ans = fread(STDIN, 1); - - if ($ans !== false) { - if (bin2hex($ans) == "7f") { - $this->action = substr($this->action, 0, -1); - } - - if (trim($ans) != "" ){ - $this->action .= trim(preg_replace("/[^A-Za-z0-9:]/", "", $ans)); - } - - if (bin2hex($ans) == "0a") { - $cmds = explode(':', $this->action); - if ($cmds[0] == "quit" || $cmds[0] == "q" || (isset($cmds[1]) && $cmds[0] == "" && $cmds[1] == "q")) { - $this->topCollector->CollectData(true); - $this->topCollector->ClearLatest(true); - - $this->terminate = true; - } - else if ($cmds[0] == "clear" ) { - $this->topCollector->ClearLatest(true); - $this->topCollector->CollectData(true); - $this->topCollector->ReInitSharedMem(); - } - else if ($cmds[0] == "filter" || $cmds[0] == "f") { - if (!isset($cmds[1]) || $cmds[1] == "") { - $this->filter = false; - $this->status = "No filter"; - $this->statusexpire = $this->currenttime+5; - } - else { - $this->filter = $cmds[1]; - $this->status = false; - } - } - else if ($cmds[0] == "option" || $cmds[0] == "o") { - if (!isset($cmds[1]) || $cmds[1] == "") { - $this->status = "Option value needs to be specified. See 'help' or 'h' for instructions"; - $this->statusexpire = $this->currenttime+5; - } - else if ($cmds[1] == "p" || $cmds[1] == "push" || $cmds[1] == "ping") - $this->showPush = !$this->showPush; - else if ($cmds[1] == "a" || $cmds[1] == "active") - $this->showOption = self::SHOW_ACTIVE_ONLY; - else if ($cmds[1] == "u" || $cmds[1] == "unknown") - $this->showOption = self::SHOW_UNKNOWN_ONLY; - else if ($cmds[1] == "d" || $cmds[1] == "default") { - $this->showOption = self::SHOW_DEFAULT; - $this->showTermSec = self::SHOW_TERM_DEFAULT_TIME; - $this->showPush = true; - } - else if (is_numeric($cmds[1])) - $this->showTermSec = $cmds[1]; - else { - $this->status = sprintf("Option '%s' unknown", $cmds[1]); - $this->statusexpire = $this->currenttime+5; - } - } - else if ($cmds[0] == "reset" || $cmds[0] == "r") { - $this->filter = false; - $this->wide = false; - $this->helpexpire = 0; - $this->status = "resetted"; - $this->statusexpire = $this->currenttime+2; - } - else if ($cmds[0] == "wide" || $cmds[0] == "w") { - $this->wide = true; - $this->status = "w i d e view"; - $this->statusexpire = $this->currenttime+2; - } - else if ($cmds[0] == "help" || $cmds[0] == "h") { - $this->helpexpire = $this->currenttime+20; - } - else if (($cmds[0] == "log" || $cmds[0] == "l") && isset($cmds[1]) ) { - if (!file_exists(LOGFILE)) { - $this->status = "Logfile can not be found: ". LOGFILE; - } - else { - system('bash -c "fgrep -a '.escapeshellarg($cmds[1]).' '. LOGFILE .' | less +G" > `tty`'); - $this->status = "Returning from log, updating data"; - } - $this->statusexpire = time()+5; // it might be much "later" now - } - else if (($cmds[0] == "tail" || $cmds[0] == "t")) { - if (!file_exists(LOGFILE)) { - $this->status = "Logfile can not be found: ". LOGFILE; - } - else { - $this->doingTail = true; - $this->scrClear(); - $this->scrPrintAt(1,0,$this->scrAsBold("Press CTRL+C to return to Z-Push-Top\n\n")); - $secondary = ""; - if (isset($cmds[1])) $secondary = " -n 200 | grep ".escapeshellarg($cmds[1]); - system('bash -c "tail -f '. LOGFILE . $secondary . '" > `tty`'); - $this->doingTail = false; - $this->status = "Returning from tail, updating data"; - } - $this->statusexpire = time()+5; // it might be much "later" now - } - - else if ($cmds[0] != "") { - $this->status = sprintf("Command '%s' unknown", $cmds[0]); - $this->statusexpire = $this->currenttime+8; - } - $this->action = ""; - } - } - } - - /** - * Signal handler function - * - * @param int $signo signal number - * - * @access public - * @return - */ - public function SignalHandler($signo) { - // don't terminate if the signal was sent by terminating tail - if (!$this->doingTail) { - $this->topCollector->CollectData(true); - $this->topCollector->ClearLatest(true); - $this->terminate = true; - } - } - - /** - * Returns usage instructions - * - * @return string - * @access public - */ - public function UsageInstructions() { - $help = "Usage:\n\tz-push-top.php\n\n" . - " Z-Push-Top is a live top-like overview of what Z-Push is doing. It does not have specific command line options.\n\n". - " When Z-Push-Top is running you can specify certain actions and options which can be executed (listed below).\n". - " This help information can also be shown inside Z-Push-Top by hitting 'help' or 'h'.\n\n"; - $scrhelp = $this->scrHelp(); - unset($scrhelp[0]); - - $help .= implode("\n", $scrhelp); - $help .= "\n\n"; - return $help; - } - - - /** - * Prints a 'help' text at the end of the page - * - * @access private - * @return array with help lines - */ - private function scrHelp() { - $h = array(); - $secs = $this->helpexpire - $this->currenttime; - $h[] = "Actions supported by Z-Push-Top (help page still displayed for ".$secs."secs)"; - $h[] = " ".$this->scrAsBold("Action")."\t\t".$this->scrAsBold("Comment"); - $h[] = " ".$this->scrAsBold("h")." or ".$this->scrAsBold("help")."\t\tDisplays this information."; - $h[] = " ".$this->scrAsBold("q").", ".$this->scrAsBold("quit")." or ".$this->scrAsBold(":q")."\t\tExits Z-Push-Top."; - $h[] = " ".$this->scrAsBold("w")." or ".$this->scrAsBold("wide")."\t\tTries not to truncate data. Automatically done if more than 180 columns available."; - $h[] = " ".$this->scrAsBold("f:VAL")." or ".$this->scrAsBold("filter:VAL")."\tOnly display connections which contain VAL. This value is case-insensitive."; - $h[] = " ".$this->scrAsBold("f:")." or ".$this->scrAsBold("filter:")."\t\tWithout a search word: resets the filter."; - $h[] = " ".$this->scrAsBold("l:STR")." or ".$this->scrAsBold("log:STR")."\tIssues 'less +G' on the logfile, after grepping on the optional STR."; - $h[] = " ".$this->scrAsBold("t:STR")." or ".$this->scrAsBold("tail:STR")."\tIssues 'tail -f' on the logfile, grepping for optional STR."; - $h[] = " ".$this->scrAsBold("r")." or ".$this->scrAsBold("reset")."\t\tResets 'wide' or 'filter'."; - $h[] = " ".$this->scrAsBold("o:")." or ".$this->scrAsBold("option:")."\t\tSets display options. Valid options specified below"; - $h[] = " ".$this->scrAsBold(" p")." or ".$this->scrAsBold("push")."\t\tLists/not lists active and open push connections."; - $h[] = " ".$this->scrAsBold(" a")." or ".$this->scrAsBold("action")."\t\tLists only active connections."; - $h[] = " ".$this->scrAsBold(" u")." or ".$this->scrAsBold("unknown")."\tLists only unknown connections."; - $h[] = " ".$this->scrAsBold(" 10")." or ".$this->scrAsBold("20")."\t\tLists terminated connections for 10 or 20 seconds. Any other number can be used."; - $h[] = " ".$this->scrAsBold(" d")." or ".$this->scrAsBold("default")."\tUses default options"; - - return $h; - } - - /** - * Encapsulates string with different color escape characters - * - * @param string $text - * - * @access private - * @return string same text as bold - */ - private function scrAsBold($text) { - return "\033[01m" . $text ."\033[0m"; - } - - /** - * Prints one line of precessed data - * - * @param array $l line information - * - * @access private - * @return string - */ - private function getLine($l) { - if ($this->wide === true) - return sprintf("%s%s%s%s%s%s%s%s", $this->ptStr($l['pid'],6), $this->ptStr($l['ip'],16), $this->ptStr($l['user'],24), $this->ptStr($l['command'],16), $this->ptStr($this->sec2min($l['time']),8), $this->ptStr($l['devagent'],28), $this->ptStr($l['devid'],33, true), $l['addinfo']); - else - return sprintf("%s%s%s%s%s%s%s%s", $this->ptStr($l['pid'],6), $this->ptStr($l['ip'],16), $this->ptStr($l['user'],8), $this->ptStr($l['command'],8), $this->ptStr($this->sec2min($l['time']),6), $this->ptStr($l['devagent'],20), $this->ptStr($l['devid'],12, true), $l['addinfo']); - } - - /** - * Pads and trims string - * - * @param string $string to be trimmed/padded - * @param int $size characters to be considered - * @param boolean $cutmiddle (optional) indicates where to long information should - * be trimmed of, false means at the end - * - * @access private - * @return string - */ - private function ptStr($str, $size, $cutmiddle = false) { - if (strlen($str) < $size) - return str_pad($str, $size); - else if ($cutmiddle == true) { - $cut = ($size-2)/2; - return $this->ptStr(substr($str,0,$cut) ."..". substr($str,(-1)*($cut-1)), $size); - } - else { - return substr($str,0,$size-3).".. "; - } - } - - /** - * Tries to discover the size of the current terminal - * - * @access private - * @return array 'width' and 'height' as keys - */ - private function scrGetSize() { - preg_match_all("/rows.([0-9]+);.columns.([0-9]+);/", strtolower(exec('stty -a | fgrep columns')), $output); - if(sizeof($output) == 3) - return array('width' => $output[2][0], 'height' => $output[1][0]); - - return array('width' => 80, 'height' => 24); - } - - /** - * Returns the version of the current Z-Push installation - * - * @access private - * @return string - */ - private function getVersion() { - return ZPUSH_VERSION; - } - - /** - * Converts seconds in MM:SS - * - * @param int $s seconds - * - * @access private - * @return string - */ - private function sec2min($s) { - if (!is_int($s)) - return $s; - return sprintf("%02.2d:%02.2d", floor($s/60), $s%60); - } - - /** - * Resets the default colors of the terminal - * - * @access private - * @return - */ - private function scrDefaultColors() { - echo "\033[0m"; - } - - /** - * Clears screen of the terminal - * - * @param array $data - * - * @access private - * @return - */ - public function scrClear() { - echo "\033[2J"; - } - - /** - * Prints a text at a specific screen/terminal coordinates - * - * @param int $row row number - * @param int $col column number - * @param string $text to be printed - * - * @access private - * @return - */ - private function scrPrintAt($row, $col, $text="") { - echo "\033[".$row.";".$col."H".$text; - } - -}