@backtomyfuture/exchange-cli-darwin-x64 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/_internal/Python +0 -0
- package/bin/_internal/Python.framework/Python +0 -0
- package/bin/_internal/Python.framework/Resources/Info.plist +30 -0
- package/bin/_internal/Python.framework/Versions/3.12/Python +0 -0
- package/bin/_internal/Python.framework/Versions/3.12/Resources/Info.plist +30 -0
- package/bin/_internal/Python.framework/Versions/Current/Python +0 -0
- package/bin/_internal/Python.framework/Versions/Current/Resources/Info.plist +30 -0
- package/bin/_internal/_cffi_backend.cpython-312-darwin.so +0 -0
- package/bin/_internal/ada92cb5d92a588d1b93__mypyc.cpython-312-darwin.so +0 -0
- package/bin/_internal/base_library.zip +0 -0
- package/bin/_internal/certifi/cacert.pem +3863 -0
- package/bin/_internal/certifi/py.typed +0 -0
- package/bin/_internal/charset_normalizer/cd.cpython-312-darwin.so +0 -0
- package/bin/_internal/charset_normalizer/md.cpython-312-darwin.so +0 -0
- package/bin/_internal/click-8.4.2.dist-info/INSTALLER +1 -0
- package/bin/_internal/click-8.4.2.dist-info/METADATA +84 -0
- package/bin/_internal/click-8.4.2.dist-info/RECORD +40 -0
- package/bin/_internal/click-8.4.2.dist-info/WHEEL +4 -0
- package/bin/_internal/click-8.4.2.dist-info/licenses/LICENSE.txt +28 -0
- package/bin/_internal/cryptography/hazmat/bindings/_rust.abi3.so +0 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/INSTALLER +1 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/METADATA +110 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/RECORD +195 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/WHEEL +4 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/licenses/LICENSE +3 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/licenses/LICENSE.APACHE +202 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/licenses/LICENSE.BSD +27 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/sboms/cryptography-rust.cyclonedx.json +1362 -0
- package/bin/_internal/cryptography-48.0.1.dist-info/sboms/sbom.json +43 -0
- package/bin/_internal/exchangelib/__init__.py +120 -0
- package/bin/_internal/exchangelib/account.py +896 -0
- package/bin/_internal/exchangelib/attachments.py +268 -0
- package/bin/_internal/exchangelib/autodiscover/__init__.py +24 -0
- package/bin/_internal/exchangelib/autodiscover/cache.py +150 -0
- package/bin/_internal/exchangelib/autodiscover/discovery.py +491 -0
- package/bin/_internal/exchangelib/autodiscover/protocol.py +58 -0
- package/bin/_internal/exchangelib/configuration.py +106 -0
- package/bin/_internal/exchangelib/credentials.py +319 -0
- package/bin/_internal/exchangelib/errors.py +1710 -0
- package/bin/_internal/exchangelib/ewsdatetime.py +309 -0
- package/bin/_internal/exchangelib/extended_properties.py +311 -0
- package/bin/_internal/exchangelib/fields.py +1842 -0
- package/bin/_internal/exchangelib/folders/__init__.py +249 -0
- package/bin/_internal/exchangelib/folders/base.py +954 -0
- package/bin/_internal/exchangelib/folders/collections.py +576 -0
- package/bin/_internal/exchangelib/folders/known_folders.py +856 -0
- package/bin/_internal/exchangelib/folders/queryset.py +187 -0
- package/bin/_internal/exchangelib/folders/roots.py +390 -0
- package/bin/_internal/exchangelib/indexed_properties.py +96 -0
- package/bin/_internal/exchangelib/items/__init__.py +147 -0
- package/bin/_internal/exchangelib/items/base.py +241 -0
- package/bin/_internal/exchangelib/items/calendar_item.py +466 -0
- package/bin/_internal/exchangelib/items/contact.py +263 -0
- package/bin/_internal/exchangelib/items/item.py +413 -0
- package/bin/_internal/exchangelib/items/message.py +201 -0
- package/bin/_internal/exchangelib/items/post.py +38 -0
- package/bin/_internal/exchangelib/items/task.py +139 -0
- package/bin/_internal/exchangelib/properties.py +2364 -0
- package/bin/_internal/exchangelib/protocol.py +823 -0
- package/bin/_internal/exchangelib/queryset.py +691 -0
- package/bin/_internal/exchangelib/recurrence.py +349 -0
- package/bin/_internal/exchangelib/restriction.py +568 -0
- package/bin/_internal/exchangelib/services/__init__.py +117 -0
- package/bin/_internal/exchangelib/services/archive_item.py +33 -0
- package/bin/_internal/exchangelib/services/common.py +983 -0
- package/bin/_internal/exchangelib/services/convert_id.py +45 -0
- package/bin/_internal/exchangelib/services/copy_item.py +7 -0
- package/bin/_internal/exchangelib/services/create_attachment.py +34 -0
- package/bin/_internal/exchangelib/services/create_folder.py +45 -0
- package/bin/_internal/exchangelib/services/create_item.py +97 -0
- package/bin/_internal/exchangelib/services/create_user_configuration.py +19 -0
- package/bin/_internal/exchangelib/services/delete_attachment.py +28 -0
- package/bin/_internal/exchangelib/services/delete_folder.py +21 -0
- package/bin/_internal/exchangelib/services/delete_item.py +51 -0
- package/bin/_internal/exchangelib/services/delete_user_configuration.py +19 -0
- package/bin/_internal/exchangelib/services/empty_folder.py +25 -0
- package/bin/_internal/exchangelib/services/expand_dl.py +21 -0
- package/bin/_internal/exchangelib/services/export_items.py +27 -0
- package/bin/_internal/exchangelib/services/find_folder.py +80 -0
- package/bin/_internal/exchangelib/services/find_item.py +112 -0
- package/bin/_internal/exchangelib/services/find_people.py +106 -0
- package/bin/_internal/exchangelib/services/get_attachment.py +117 -0
- package/bin/_internal/exchangelib/services/get_delegate.py +45 -0
- package/bin/_internal/exchangelib/services/get_events.py +39 -0
- package/bin/_internal/exchangelib/services/get_folder.py +65 -0
- package/bin/_internal/exchangelib/services/get_item.py +41 -0
- package/bin/_internal/exchangelib/services/get_mail_tips.py +43 -0
- package/bin/_internal/exchangelib/services/get_persona.py +31 -0
- package/bin/_internal/exchangelib/services/get_room_lists.py +21 -0
- package/bin/_internal/exchangelib/services/get_rooms.py +21 -0
- package/bin/_internal/exchangelib/services/get_searchable_mailboxes.py +48 -0
- package/bin/_internal/exchangelib/services/get_server_time_zones.py +39 -0
- package/bin/_internal/exchangelib/services/get_streaming_events.py +102 -0
- package/bin/_internal/exchangelib/services/get_user_availability.py +67 -0
- package/bin/_internal/exchangelib/services/get_user_configuration.py +43 -0
- package/bin/_internal/exchangelib/services/get_user_oof_settings.py +40 -0
- package/bin/_internal/exchangelib/services/get_user_settings.py +87 -0
- package/bin/_internal/exchangelib/services/inbox_rules.py +104 -0
- package/bin/_internal/exchangelib/services/mark_as_junk.py +27 -0
- package/bin/_internal/exchangelib/services/move_folder.py +27 -0
- package/bin/_internal/exchangelib/services/move_item.py +28 -0
- package/bin/_internal/exchangelib/services/resolve_names.py +104 -0
- package/bin/_internal/exchangelib/services/send_item.py +26 -0
- package/bin/_internal/exchangelib/services/send_notification.py +43 -0
- package/bin/_internal/exchangelib/services/set_user_oof_settings.py +34 -0
- package/bin/_internal/exchangelib/services/subscribe.py +125 -0
- package/bin/_internal/exchangelib/services/sync_folder_hierarchy.py +95 -0
- package/bin/_internal/exchangelib/services/sync_folder_items.py +77 -0
- package/bin/_internal/exchangelib/services/unsubscribe.py +21 -0
- package/bin/_internal/exchangelib/services/update_folder.py +158 -0
- package/bin/_internal/exchangelib/services/update_item.py +113 -0
- package/bin/_internal/exchangelib/services/update_user_configuration.py +17 -0
- package/bin/_internal/exchangelib/services/upload_items.py +47 -0
- package/bin/_internal/exchangelib/settings.py +88 -0
- package/bin/_internal/exchangelib/transport.py +214 -0
- package/bin/_internal/exchangelib/util.py +894 -0
- package/bin/_internal/exchangelib/version.py +320 -0
- package/bin/_internal/exchangelib/winzone.py +662 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/INSTALLER +1 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/METADATA +77 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/RECORD +184 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/WHEEL +5 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/licenses/LICENSE +21 -0
- package/bin/_internal/exchangelib-5.6.0.dist-info/top_level.txt +1 -0
- package/bin/_internal/libcrypto.3.dylib +0 -0
- package/bin/_internal/libssl.3.dylib +0 -0
- package/bin/_internal/lxml/_elementpath.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/builder.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/etree.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/html/_difflib.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/html/diff.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/isoschematron/resources/rng/iso-schematron.rng +709 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl +75 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl +77 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl +313 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl +1160 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1796 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +588 -0
- package/bin/_internal/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt +84 -0
- package/bin/_internal/lxml/objectify.cpython-312-darwin.so +0 -0
- package/bin/_internal/lxml/sax.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_asyncio.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_bisect.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_blake2.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_bz2.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_cn.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_hk.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_iso2022.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_jp.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_kr.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_codecs_tw.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_contextvars.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_csv.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_ctypes.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_datetime.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_dbm.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_decimal.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_elementtree.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_hashlib.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_heapq.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_json.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_lzma.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_md5.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_multibytecodec.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_multiprocessing.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_opcode.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_pickle.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_posixshmem.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_posixsubprocess.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_queue.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_random.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_scproxy.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_sha1.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_sha2.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_sha3.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_socket.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_ssl.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_statistics.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_struct.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_uuid.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/_zoneinfo.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/array.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/binascii.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/fcntl.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/grp.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/math.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/mmap.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/pyexpat.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/readline.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/resource.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/select.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/syslog.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/termios.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/unicodedata.cpython-312-darwin.so +0 -0
- package/bin/_internal/python3.12/lib-dynload/zlib.cpython-312-darwin.so +0 -0
- package/bin/_internal/setuptools/_vendor/jaraco/text/Lorem ipsum.txt +2 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Abidjan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Accra +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Addis_Ababa +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Algiers +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Asmara +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Asmera +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Bamako +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Bangui +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Banjul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Bissau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Blantyre +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Brazzaville +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Bujumbura +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Cairo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Casablanca +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Ceuta +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Conakry +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Dakar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Dar_es_Salaam +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Djibouti +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Douala +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/El_Aaiun +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Freetown +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Gaborone +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Harare +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Johannesburg +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Juba +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Kampala +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Khartoum +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Kigali +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Kinshasa +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Lagos +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Libreville +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Lome +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Luanda +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Lubumbashi +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Lusaka +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Malabo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Maputo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Maseru +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Mbabane +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Mogadishu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Monrovia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Nairobi +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Ndjamena +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Niamey +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Nouakchott +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Ouagadougou +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Porto-Novo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Sao_Tome +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Timbuktu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Tripoli +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Tunis +0 -0
- package/bin/_internal/tzdata/zoneinfo/Africa/Windhoek +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Adak +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Anchorage +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Anguilla +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Antigua +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Araguaina +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Buenos_Aires +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Catamarca +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/ComodRivadavia +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Cordoba +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Jujuy +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/La_Rioja +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Mendoza +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Rio_Gallegos +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Salta +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/San_Juan +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/San_Luis +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Tucuman +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Argentina/Ushuaia +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Aruba +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Asuncion +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Atikokan +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Atka +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Bahia +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Bahia_Banderas +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Barbados +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Belem +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Belize +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Blanc-Sablon +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Boa_Vista +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Bogota +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Boise +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Buenos_Aires +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cambridge_Bay +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Campo_Grande +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cancun +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Caracas +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Catamarca +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cayenne +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cayman +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Chicago +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Chihuahua +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Ciudad_Juarez +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Coral_Harbour +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cordoba +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Costa_Rica +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Coyhaique +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Creston +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Cuiaba +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Curacao +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Danmarkshavn +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Dawson +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Dawson_Creek +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Denver +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Detroit +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Dominica +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Edmonton +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Eirunepe +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/El_Salvador +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Ensenada +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Fort_Nelson +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Fort_Wayne +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Fortaleza +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Glace_Bay +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Godthab +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Goose_Bay +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Grand_Turk +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Grenada +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Guadeloupe +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Guatemala +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Guayaquil +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Guyana +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Halifax +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Havana +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Hermosillo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Indianapolis +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Knox +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Marengo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Petersburg +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Tell_City +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Vevay +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Vincennes +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indiana/Winamac +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Indianapolis +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Inuvik +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Iqaluit +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Jamaica +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Jujuy +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Juneau +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Kentucky/Louisville +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Kentucky/Monticello +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Knox_IN +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Kralendijk +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/La_Paz +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Lima +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Los_Angeles +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Louisville +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Lower_Princes +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Maceio +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Managua +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Manaus +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Marigot +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Martinique +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Matamoros +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Mazatlan +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Mendoza +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Menominee +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Merida +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Metlakatla +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Mexico_City +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Miquelon +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Moncton +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Monterrey +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Montevideo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Montreal +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Montserrat +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Nassau +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/New_York +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Nipigon +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Nome +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Noronha +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/North_Dakota/Beulah +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/North_Dakota/Center +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/North_Dakota/New_Salem +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Nuuk +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Ojinaga +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Panama +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Pangnirtung +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Paramaribo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Phoenix +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Port-au-Prince +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Port_of_Spain +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Porto_Acre +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Porto_Velho +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Puerto_Rico +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Punta_Arenas +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Rainy_River +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Rankin_Inlet +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Recife +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Regina +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Resolute +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Rio_Branco +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Rosario +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Santa_Isabel +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Santarem +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Santiago +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Santo_Domingo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Sao_Paulo +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Scoresbysund +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Shiprock +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Sitka +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Barthelemy +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Johns +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Kitts +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Lucia +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Thomas +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/St_Vincent +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Swift_Current +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Tegucigalpa +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Thule +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Thunder_Bay +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Tijuana +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Toronto +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Tortola +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Vancouver +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Virgin +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Whitehorse +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Winnipeg +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Yakutat +0 -0
- package/bin/_internal/tzdata/zoneinfo/America/Yellowknife +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Casey +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Davis +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/DumontDUrville +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Macquarie +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Mawson +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/McMurdo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Palmer +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Rothera +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/South_Pole +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Syowa +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Troll +0 -0
- package/bin/_internal/tzdata/zoneinfo/Antarctica/Vostok +0 -0
- package/bin/_internal/tzdata/zoneinfo/Arctic/Longyearbyen +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Aden +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Almaty +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Amman +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Anadyr +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Aqtau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Aqtobe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ashgabat +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ashkhabad +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Atyrau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Baghdad +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Bahrain +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Baku +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Bangkok +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Barnaul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Beirut +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Bishkek +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Brunei +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Calcutta +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Chita +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Choibalsan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Chongqing +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Chungking +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Colombo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Dacca +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Damascus +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Dhaka +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Dili +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Dubai +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Dushanbe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Famagusta +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Gaza +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Harbin +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Hebron +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ho_Chi_Minh +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Hong_Kong +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Hovd +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Irkutsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Istanbul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Jakarta +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Jayapura +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Jerusalem +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kabul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kamchatka +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Karachi +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kashgar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kathmandu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Katmandu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Khandyga +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kolkata +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Krasnoyarsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kuala_Lumpur +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kuching +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Kuwait +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Macao +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Macau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Magadan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Makassar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Manila +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Muscat +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Nicosia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Novokuznetsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Novosibirsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Omsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Oral +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Phnom_Penh +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Pontianak +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Pyongyang +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Qatar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Qostanay +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Qyzylorda +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Rangoon +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Riyadh +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Saigon +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Sakhalin +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Samarkand +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Seoul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Shanghai +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Singapore +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Srednekolymsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Taipei +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tashkent +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tbilisi +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tehran +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tel_Aviv +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Thimbu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Thimphu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tokyo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Tomsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ujung_Pandang +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ulaanbaatar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ulan_Bator +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Urumqi +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Ust-Nera +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Vientiane +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Vladivostok +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Yakutsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Yangon +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Yekaterinburg +0 -0
- package/bin/_internal/tzdata/zoneinfo/Asia/Yerevan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Azores +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Bermuda +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Canary +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Cape_Verde +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Faeroe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Faroe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Jan_Mayen +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Madeira +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Reykjavik +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/South_Georgia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/St_Helena +0 -0
- package/bin/_internal/tzdata/zoneinfo/Atlantic/Stanley +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/ACT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Adelaide +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Brisbane +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Broken_Hill +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Canberra +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Currie +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Darwin +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Eucla +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Hobart +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/LHI +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Lindeman +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Lord_Howe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Melbourne +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/NSW +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/North +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Perth +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Queensland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/South +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Sydney +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Tasmania +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Victoria +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/West +0 -0
- package/bin/_internal/tzdata/zoneinfo/Australia/Yancowinna +0 -0
- package/bin/_internal/tzdata/zoneinfo/Brazil/Acre +0 -0
- package/bin/_internal/tzdata/zoneinfo/Brazil/DeNoronha +0 -0
- package/bin/_internal/tzdata/zoneinfo/Brazil/East +0 -0
- package/bin/_internal/tzdata/zoneinfo/Brazil/West +0 -0
- package/bin/_internal/tzdata/zoneinfo/CET +0 -0
- package/bin/_internal/tzdata/zoneinfo/CST6CDT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Atlantic +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Central +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Eastern +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Mountain +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Newfoundland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Pacific +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Saskatchewan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Canada/Yukon +0 -0
- package/bin/_internal/tzdata/zoneinfo/Chile/Continental +0 -0
- package/bin/_internal/tzdata/zoneinfo/Chile/EasterIsland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Cuba +0 -0
- package/bin/_internal/tzdata/zoneinfo/EET +0 -0
- package/bin/_internal/tzdata/zoneinfo/EST +0 -0
- package/bin/_internal/tzdata/zoneinfo/EST5EDT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Egypt +0 -0
- package/bin/_internal/tzdata/zoneinfo/Eire +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+1 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+10 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+11 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+12 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+2 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+3 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+4 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+5 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+6 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+7 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+8 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT+9 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-1 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-10 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-11 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-12 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-13 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-14 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-2 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-3 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-4 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-5 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-6 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-7 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-8 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT-9 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/GMT0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/Greenwich +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/UCT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/UTC +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/Universal +0 -0
- package/bin/_internal/tzdata/zoneinfo/Etc/Zulu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Amsterdam +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Andorra +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Astrakhan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Athens +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Belfast +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Belgrade +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Berlin +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Bratislava +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Brussels +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Bucharest +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Budapest +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Busingen +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Chisinau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Copenhagen +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Dublin +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Gibraltar +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Guernsey +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Helsinki +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Isle_of_Man +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Istanbul +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Jersey +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Kaliningrad +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Kiev +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Kirov +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Kyiv +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Lisbon +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Ljubljana +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/London +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Luxembourg +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Madrid +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Malta +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Mariehamn +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Minsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Monaco +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Moscow +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Nicosia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Oslo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Paris +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Podgorica +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Prague +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Riga +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Rome +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Samara +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/San_Marino +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Sarajevo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Saratov +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Simferopol +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Skopje +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Sofia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Stockholm +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Tallinn +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Tirane +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Tiraspol +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Ulyanovsk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Uzhgorod +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Vaduz +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Vatican +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Vienna +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Vilnius +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Volgograd +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Warsaw +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Zagreb +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Zaporozhye +0 -0
- package/bin/_internal/tzdata/zoneinfo/Europe/Zurich +0 -0
- package/bin/_internal/tzdata/zoneinfo/Factory +0 -0
- package/bin/_internal/tzdata/zoneinfo/GB +0 -0
- package/bin/_internal/tzdata/zoneinfo/GB-Eire +0 -0
- package/bin/_internal/tzdata/zoneinfo/GMT +0 -0
- package/bin/_internal/tzdata/zoneinfo/GMT+0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/GMT-0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/GMT0 +0 -0
- package/bin/_internal/tzdata/zoneinfo/Greenwich +0 -0
- package/bin/_internal/tzdata/zoneinfo/HST +0 -0
- package/bin/_internal/tzdata/zoneinfo/Hongkong +0 -0
- package/bin/_internal/tzdata/zoneinfo/Iceland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Antananarivo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Chagos +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Christmas +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Cocos +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Comoro +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Kerguelen +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Mahe +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Maldives +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Mauritius +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Mayotte +0 -0
- package/bin/_internal/tzdata/zoneinfo/Indian/Reunion +0 -0
- package/bin/_internal/tzdata/zoneinfo/Iran +0 -0
- package/bin/_internal/tzdata/zoneinfo/Israel +0 -0
- package/bin/_internal/tzdata/zoneinfo/Jamaica +0 -0
- package/bin/_internal/tzdata/zoneinfo/Japan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Kwajalein +0 -0
- package/bin/_internal/tzdata/zoneinfo/Libya +0 -0
- package/bin/_internal/tzdata/zoneinfo/MET +0 -0
- package/bin/_internal/tzdata/zoneinfo/MST +0 -0
- package/bin/_internal/tzdata/zoneinfo/MST7MDT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Mexico/BajaNorte +0 -0
- package/bin/_internal/tzdata/zoneinfo/Mexico/BajaSur +0 -0
- package/bin/_internal/tzdata/zoneinfo/Mexico/General +0 -0
- package/bin/_internal/tzdata/zoneinfo/NZ +0 -0
- package/bin/_internal/tzdata/zoneinfo/NZ-CHAT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Navajo +0 -0
- package/bin/_internal/tzdata/zoneinfo/PRC +0 -0
- package/bin/_internal/tzdata/zoneinfo/PST8PDT +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Apia +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Auckland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Bougainville +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Chatham +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Chuuk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Easter +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Efate +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Enderbury +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Fakaofo +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Fiji +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Funafuti +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Galapagos +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Gambier +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Guadalcanal +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Guam +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Honolulu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Johnston +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Kanton +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Kiritimati +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Kosrae +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Kwajalein +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Majuro +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Marquesas +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Midway +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Nauru +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Niue +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Norfolk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Noumea +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Pago_Pago +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Palau +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Pitcairn +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Pohnpei +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Ponape +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Port_Moresby +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Rarotonga +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Saipan +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Samoa +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Tahiti +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Tarawa +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Tongatapu +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Truk +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Wake +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Wallis +0 -0
- package/bin/_internal/tzdata/zoneinfo/Pacific/Yap +0 -0
- package/bin/_internal/tzdata/zoneinfo/Poland +0 -0
- package/bin/_internal/tzdata/zoneinfo/Portugal +0 -0
- package/bin/_internal/tzdata/zoneinfo/ROC +0 -0
- package/bin/_internal/tzdata/zoneinfo/ROK +0 -0
- package/bin/_internal/tzdata/zoneinfo/Singapore +0 -0
- package/bin/_internal/tzdata/zoneinfo/Turkey +0 -0
- package/bin/_internal/tzdata/zoneinfo/UCT +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Alaska +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Aleutian +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Arizona +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Central +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/East-Indiana +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Eastern +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Hawaii +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Indiana-Starke +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Michigan +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Mountain +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Pacific +0 -0
- package/bin/_internal/tzdata/zoneinfo/US/Samoa +0 -0
- package/bin/_internal/tzdata/zoneinfo/UTC +0 -0
- package/bin/_internal/tzdata/zoneinfo/Universal +0 -0
- package/bin/_internal/tzdata/zoneinfo/W-SU +0 -0
- package/bin/_internal/tzdata/zoneinfo/WET +0 -0
- package/bin/_internal/tzdata/zoneinfo/Zulu +0 -0
- package/bin/_internal/tzdata/zoneinfo/iso3166.tab +279 -0
- package/bin/_internal/tzdata/zoneinfo/leapseconds +86 -0
- package/bin/_internal/tzdata/zoneinfo/tzdata.zi +4180 -0
- package/bin/_internal/tzdata/zoneinfo/zone.tab +448 -0
- package/bin/_internal/tzdata/zoneinfo/zone1970.tab +375 -0
- package/bin/_internal/tzdata/zoneinfo/zonenow.tab +299 -0
- package/bin/_internal/tzdata/zones +598 -0
- package/bin/exchange-cli +0 -0
- package/package.json +20 -0
|
@@ -0,0 +1,1842 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import datetime
|
|
3
|
+
import logging
|
|
4
|
+
import warnings
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
from decimal import Decimal, InvalidOperation
|
|
7
|
+
from importlib import import_module
|
|
8
|
+
|
|
9
|
+
from .errors import InvalidTypeError
|
|
10
|
+
from .ewsdatetime import UTC, EWSDate, EWSDateTime, EWSTimeZone, NaiveDateTimeNotAllowed, UnknownTimeZone
|
|
11
|
+
from .util import (
|
|
12
|
+
TNS,
|
|
13
|
+
create_element,
|
|
14
|
+
get_xml_attr,
|
|
15
|
+
get_xml_attrs,
|
|
16
|
+
is_iterable,
|
|
17
|
+
set_xml_value,
|
|
18
|
+
value_to_xml_text,
|
|
19
|
+
xml_text_to_value,
|
|
20
|
+
)
|
|
21
|
+
from .version import EXCHANGE_2013, SupportedVersionInstanceMixIn
|
|
22
|
+
|
|
23
|
+
log = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# DayOfWeekIndex enum. See
|
|
27
|
+
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/dayofweekindex
|
|
28
|
+
FIRST = "First"
|
|
29
|
+
SECOND = "Second"
|
|
30
|
+
THIRD = "Third"
|
|
31
|
+
FOURTH = "Fourth"
|
|
32
|
+
LAST = "Last"
|
|
33
|
+
WEEK_NUMBERS = (FIRST, SECOND, THIRD, FOURTH, LAST)
|
|
34
|
+
|
|
35
|
+
# Month enum
|
|
36
|
+
JANUARY = "January"
|
|
37
|
+
FEBRUARY = "February"
|
|
38
|
+
MARCH = "March"
|
|
39
|
+
APRIL = "April"
|
|
40
|
+
MAY = "May"
|
|
41
|
+
JUNE = "June"
|
|
42
|
+
JULY = "July"
|
|
43
|
+
AUGUST = "August"
|
|
44
|
+
SEPTEMBER = "September"
|
|
45
|
+
OCTOBER = "October"
|
|
46
|
+
NOVEMBER = "November"
|
|
47
|
+
DECEMBER = "December"
|
|
48
|
+
MONTHS = (JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER)
|
|
49
|
+
|
|
50
|
+
# Weekday enum
|
|
51
|
+
MONDAY = "Monday"
|
|
52
|
+
TUESDAY = "Tuesday"
|
|
53
|
+
WEDNESDAY = "Wednesday"
|
|
54
|
+
THURSDAY = "Thursday"
|
|
55
|
+
FRIDAY = "Friday"
|
|
56
|
+
SATURDAY = "Saturday"
|
|
57
|
+
SUNDAY = "Sunday"
|
|
58
|
+
WEEKDAY_NAMES = (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY)
|
|
59
|
+
|
|
60
|
+
# Used for weekday recurrences except weekly recurrences. E.g. for "First WeekendDay in March"
|
|
61
|
+
DAY = "Day"
|
|
62
|
+
WEEK_DAY = "Weekday" # Non-weekend day
|
|
63
|
+
WEEKEND_DAY = "WeekendDay"
|
|
64
|
+
EXTRA_WEEKDAY_OPTIONS = (DAY, WEEK_DAY, WEEKEND_DAY)
|
|
65
|
+
|
|
66
|
+
# DaysOfWeek enum: See
|
|
67
|
+
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/daysofweek-daysofweektype
|
|
68
|
+
WEEKDAYS = WEEKDAY_NAMES + EXTRA_WEEKDAY_OPTIONS
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InvalidField(ValueError):
|
|
72
|
+
"""Used when a field name does not match any defined fields."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class InvalidFieldForVersion(ValueError):
|
|
76
|
+
"""Used when a field is not supported on the given Exchange version."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class InvalidChoiceForVersion(ValueError):
|
|
80
|
+
"""Used when a value is not valid for an enum-type field."""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def split_field_path(field_path):
|
|
84
|
+
"""Split a string path into its field, label and subfield parts.
|
|
85
|
+
|
|
86
|
+
:param field_path:
|
|
87
|
+
|
|
88
|
+
:return Examples:
|
|
89
|
+
'start' -> ('start', None, None)
|
|
90
|
+
'phone_numbers__PrimaryPhone' -> ('phone_numbers', 'PrimaryPhone', None)
|
|
91
|
+
'physical_addresses__Home__street' -> ('physical_addresses', 'Home', 'street')
|
|
92
|
+
"""
|
|
93
|
+
if not isinstance(field_path, str):
|
|
94
|
+
raise InvalidTypeError("field_path", field_path, str)
|
|
95
|
+
search_parts = field_path.split("__")
|
|
96
|
+
field = search_parts[0]
|
|
97
|
+
try:
|
|
98
|
+
label = search_parts[1]
|
|
99
|
+
except IndexError:
|
|
100
|
+
label = None
|
|
101
|
+
try:
|
|
102
|
+
subfield = search_parts[2]
|
|
103
|
+
except IndexError:
|
|
104
|
+
subfield = None
|
|
105
|
+
return field, label, subfield
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def resolve_field_path(field_path, folder, strict=True):
|
|
109
|
+
"""Take the name of a field, or '__'-delimited path to a subfield, and return the corresponding Field object,
|
|
110
|
+
label and SubField object.
|
|
111
|
+
"""
|
|
112
|
+
from .indexed_properties import MultiFieldIndexedElement, SingleFieldIndexedElement
|
|
113
|
+
|
|
114
|
+
fieldname, label, subfield_name = split_field_path(field_path)
|
|
115
|
+
field = folder.get_item_field_by_fieldname(fieldname)
|
|
116
|
+
subfield = None
|
|
117
|
+
if isinstance(field, IndexedField):
|
|
118
|
+
if strict and not label:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"IndexedField path {field_path!r} must specify label, e.g. "
|
|
121
|
+
f"'{fieldname}__{field.value_cls.get_field_by_fieldname('label').default}'"
|
|
122
|
+
)
|
|
123
|
+
valid_labels = field.value_cls.get_field_by_fieldname("label").supported_choices(version=folder.account.version)
|
|
124
|
+
if label and label not in valid_labels:
|
|
125
|
+
raise ValueError(
|
|
126
|
+
f"Label {label!r} on IndexedField path {field_path!r} must be one of {sorted(valid_labels)}"
|
|
127
|
+
)
|
|
128
|
+
if issubclass(field.value_cls, MultiFieldIndexedElement):
|
|
129
|
+
if strict and not subfield_name:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"IndexedField path {field_path!r} must specify subfield, e.g. "
|
|
132
|
+
f"'{fieldname}__{label}__{field.value_cls.FIELDS[1].name}'"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if subfield_name:
|
|
136
|
+
try:
|
|
137
|
+
subfield = field.value_cls.get_field_by_fieldname(subfield_name)
|
|
138
|
+
except ValueError:
|
|
139
|
+
field_names = ", ".join(
|
|
140
|
+
f.name for f in field.value_cls.supported_fields(version=folder.account.version)
|
|
141
|
+
)
|
|
142
|
+
raise ValueError(
|
|
143
|
+
f"Subfield {subfield_name!r} on IndexedField path {field_path!r} "
|
|
144
|
+
f"must be one of {sorted(field_names)}"
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
if not issubclass(field.value_cls, SingleFieldIndexedElement):
|
|
148
|
+
raise InvalidTypeError("field.value_cls", field.value_cls, SingleFieldIndexedElement)
|
|
149
|
+
if subfield_name:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"IndexedField path {field_path!r} must not specify subfield, e.g. just {fieldname}__{label}'"
|
|
152
|
+
)
|
|
153
|
+
subfield = field.value_cls.value_field(version=folder.account.version)
|
|
154
|
+
else:
|
|
155
|
+
if label or subfield_name:
|
|
156
|
+
raise ValueError(f"Field path {field_path!r} must not specify label or subfield, e.g. just {fieldname!r}")
|
|
157
|
+
return field, label, subfield
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class FieldPath:
|
|
161
|
+
"""Holds values needed to point to a single field. For indexed properties, we allow setting either field,
|
|
162
|
+
field and label, or field, label and subfield. This allows pointing to either the full indexed property set, a
|
|
163
|
+
property with a specific label, or a particular subfield field on that property.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def __init__(self, field, label=None, subfield=None):
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
:param field: A FieldURIField or ExtendedPropertyField instance
|
|
170
|
+
:param label: a str
|
|
171
|
+
:param subfield: A SubField instance
|
|
172
|
+
"""
|
|
173
|
+
# 'label' and 'subfield' are only used for IndexedField fields
|
|
174
|
+
self.field = field
|
|
175
|
+
self.label = label
|
|
176
|
+
self.subfield = subfield
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def from_string(cls, field_path, folder, strict=False):
|
|
180
|
+
field, label, subfield = resolve_field_path(field_path, folder=folder, strict=strict)
|
|
181
|
+
return cls(field=field, label=label, subfield=subfield)
|
|
182
|
+
|
|
183
|
+
def get_value(self, item):
|
|
184
|
+
# For indexed properties, get either the full property set, the property with matching label, or a particular
|
|
185
|
+
# subfield.
|
|
186
|
+
if self.label:
|
|
187
|
+
for sub_item in getattr(item, self.field.name):
|
|
188
|
+
if sub_item.label == self.label:
|
|
189
|
+
if self.subfield:
|
|
190
|
+
return getattr(sub_item, self.subfield.name)
|
|
191
|
+
return sub_item
|
|
192
|
+
return None # No item with this label
|
|
193
|
+
return getattr(item, self.field.name)
|
|
194
|
+
|
|
195
|
+
def get_sort_value(self, item):
|
|
196
|
+
# For fields that allow values of different types, we need to return a value that is
|
|
197
|
+
val = self.get_value(item)
|
|
198
|
+
if isinstance(self.field, DateOrDateTimeField) and isinstance(val, EWSDate):
|
|
199
|
+
return item.date_to_datetime(field_name=self.field.name)
|
|
200
|
+
return val
|
|
201
|
+
|
|
202
|
+
def to_xml(self):
|
|
203
|
+
if isinstance(self.field, IndexedField):
|
|
204
|
+
if not self.label or not self.subfield:
|
|
205
|
+
raise ValueError(f"Field path for indexed field {self.field.name!r} is missing label and/or subfield")
|
|
206
|
+
return self.subfield.field_uri_xml(field_uri=self.field.field_uri, label=self.label)
|
|
207
|
+
return self.field.field_uri_xml()
|
|
208
|
+
|
|
209
|
+
def expand(self, version):
|
|
210
|
+
# If this path does not point to a specific subfield on an indexed property, return all the possible path
|
|
211
|
+
# combinations for this field path.
|
|
212
|
+
if isinstance(self.field, IndexedField):
|
|
213
|
+
labels = (
|
|
214
|
+
[self.label]
|
|
215
|
+
if self.label
|
|
216
|
+
else self.field.value_cls.get_field_by_fieldname("label").supported_choices(version=version)
|
|
217
|
+
)
|
|
218
|
+
subfields = [self.subfield] if self.subfield else self.field.value_cls.supported_fields(version=version)
|
|
219
|
+
for label in labels:
|
|
220
|
+
for subfield in subfields:
|
|
221
|
+
yield FieldPath(field=self.field, label=label, subfield=subfield)
|
|
222
|
+
else:
|
|
223
|
+
yield self
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def path(self):
|
|
227
|
+
if self.label:
|
|
228
|
+
from .indexed_properties import SingleFieldIndexedElement
|
|
229
|
+
|
|
230
|
+
if issubclass(self.field.value_cls, SingleFieldIndexedElement) or not self.subfield:
|
|
231
|
+
return f"{self.field.name}__{self.label}"
|
|
232
|
+
return f"{self.field.name}__{self.label}__{self.subfield.name}"
|
|
233
|
+
return self.field.name
|
|
234
|
+
|
|
235
|
+
def __eq__(self, other):
|
|
236
|
+
return hash(self) == hash(other)
|
|
237
|
+
|
|
238
|
+
def __str__(self):
|
|
239
|
+
return self.path
|
|
240
|
+
|
|
241
|
+
def __repr__(self):
|
|
242
|
+
return self.__class__.__name__ + repr((self.field, self.label, self.subfield))
|
|
243
|
+
|
|
244
|
+
def __hash__(self):
|
|
245
|
+
return hash((self.field, self.label, self.subfield))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class FieldOrder:
|
|
249
|
+
"""Holds values needed to call server-side sorting on a single field path."""
|
|
250
|
+
|
|
251
|
+
def __init__(self, field_path, reverse=False):
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
:param field_path: A FieldPath instance
|
|
255
|
+
:param reverse: A bool
|
|
256
|
+
"""
|
|
257
|
+
self.field_path = field_path
|
|
258
|
+
self.reverse = reverse
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def from_string(cls, field_path, folder):
|
|
262
|
+
return cls(
|
|
263
|
+
field_path=FieldPath.from_string(field_path=field_path.lstrip("-"), folder=folder, strict=True),
|
|
264
|
+
reverse=field_path.startswith("-"),
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def to_xml(self):
|
|
268
|
+
field_order = create_element("t:FieldOrder", attrs=dict(Order="Descending" if self.reverse else "Ascending"))
|
|
269
|
+
field_order.append(self.field_path.to_xml())
|
|
270
|
+
return field_order
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class Field(SupportedVersionInstanceMixIn, metaclass=abc.ABCMeta):
|
|
274
|
+
"""Holds information related to an item field."""
|
|
275
|
+
|
|
276
|
+
value_cls = None
|
|
277
|
+
is_list = False
|
|
278
|
+
# Is the field a complex EWS type? Quoting the EWS FindItem docs:
|
|
279
|
+
#
|
|
280
|
+
# The FindItem operation returns only the first 512 bytes of any streamable property. For Unicode, it returns
|
|
281
|
+
# the first 255 characters by using a null-terminated Unicode string. It does not return any of the message
|
|
282
|
+
# body formats or the recipient lists.
|
|
283
|
+
#
|
|
284
|
+
is_complex = False
|
|
285
|
+
|
|
286
|
+
def __init__(
|
|
287
|
+
self,
|
|
288
|
+
name=None,
|
|
289
|
+
is_required=False,
|
|
290
|
+
is_required_after_save=False,
|
|
291
|
+
is_read_only=False,
|
|
292
|
+
is_read_only_after_send=False,
|
|
293
|
+
is_searchable=True,
|
|
294
|
+
is_attribute=False,
|
|
295
|
+
default=None,
|
|
296
|
+
**kwargs,
|
|
297
|
+
):
|
|
298
|
+
self.name = name # Usually set by the EWSMeta metaclass
|
|
299
|
+
self.default = default # Default value if none is given
|
|
300
|
+
self.is_required = is_required
|
|
301
|
+
# Some fields cannot be deleted on update. Default to True if 'is_required' is set
|
|
302
|
+
self.is_required_after_save = is_required or is_required_after_save
|
|
303
|
+
self.is_read_only = is_read_only
|
|
304
|
+
# Set this for fields that raise ErrorInvalidPropertyUpdateSentMessage on update after send. Default to True
|
|
305
|
+
# if 'is_read_only' is set
|
|
306
|
+
self.is_read_only_after_send = is_read_only or is_read_only_after_send
|
|
307
|
+
# Define whether the field can be used in a QuerySet. For some reason, EWS disallows searching on some fields,
|
|
308
|
+
# instead throwing ErrorInvalidValueForProperty
|
|
309
|
+
self.is_searchable = is_searchable
|
|
310
|
+
# When true, this field is treated as an XML attribute instead of an element
|
|
311
|
+
self.is_attribute = is_attribute
|
|
312
|
+
super().__init__(**kwargs)
|
|
313
|
+
|
|
314
|
+
def clean(self, value, version=None):
|
|
315
|
+
if version and not self.supports_version(version):
|
|
316
|
+
raise InvalidFieldForVersion(
|
|
317
|
+
f"Field {self.name!r} does not support EWS builds prior to {self.supported_from} (server has {version})"
|
|
318
|
+
)
|
|
319
|
+
if value is None:
|
|
320
|
+
if self.is_required and self.default is None:
|
|
321
|
+
raise ValueError(f"{self.name!r} is a required field with no default")
|
|
322
|
+
return self.default
|
|
323
|
+
if self.is_list:
|
|
324
|
+
if not is_iterable(value):
|
|
325
|
+
raise TypeError(f"Field {self.name!r} value {value!r} must be of type {list}")
|
|
326
|
+
for v in value:
|
|
327
|
+
if not isinstance(v, self.value_cls):
|
|
328
|
+
raise TypeError(f"Field {self.name!r} value {v!r} must be of type {self.value_cls}")
|
|
329
|
+
if hasattr(v, "clean"):
|
|
330
|
+
v.clean(version=version)
|
|
331
|
+
else:
|
|
332
|
+
if not isinstance(value, self.value_cls):
|
|
333
|
+
raise TypeError(f"Field {self.name!r} value {value!r} must be of type {self.value_cls}")
|
|
334
|
+
if hasattr(value, "clean"):
|
|
335
|
+
value.clean(version=version)
|
|
336
|
+
return value
|
|
337
|
+
|
|
338
|
+
@abc.abstractmethod
|
|
339
|
+
def from_xml(self, elem, account):
|
|
340
|
+
"""Read a value from the given element"""
|
|
341
|
+
|
|
342
|
+
@abc.abstractmethod
|
|
343
|
+
def to_xml(self, value, version):
|
|
344
|
+
"""Convert this field to an XML element"""
|
|
345
|
+
|
|
346
|
+
def __eq__(self, other):
|
|
347
|
+
return hash(self) == hash(other)
|
|
348
|
+
|
|
349
|
+
@abc.abstractmethod
|
|
350
|
+
def __hash__(self):
|
|
351
|
+
"""Field instances must be hashable"""
|
|
352
|
+
|
|
353
|
+
def __repr__(self):
|
|
354
|
+
args_str = ", ".join(
|
|
355
|
+
f"{f}={getattr(self, f)!r}" for f in ("name", "value_cls", "is_list", "is_complex", "default")
|
|
356
|
+
)
|
|
357
|
+
return f"{self.__class__.__name__}({args_str})"
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class FieldURIField(Field):
|
|
361
|
+
"""A field that has a FieldURI value in EWS. This means it's value is contained in an XML element or attribute. It
|
|
362
|
+
may additionally be a label for searching, filtering and limiting fields. In that case, the FieldURI format will be
|
|
363
|
+
'itemtype:FieldName'
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
def __init__(self, *args, **kwargs):
|
|
367
|
+
self.field_uri = kwargs.pop("field_uri", None)
|
|
368
|
+
self.namespace = kwargs.pop("namespace", TNS)
|
|
369
|
+
super().__init__(*args, **kwargs)
|
|
370
|
+
# See all valid FieldURI values at
|
|
371
|
+
# https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/fielduri
|
|
372
|
+
# The field_uri has a prefix when the FieldURI points to an Item field.
|
|
373
|
+
if self.field_uri is None:
|
|
374
|
+
self.field_uri_postfix = None
|
|
375
|
+
elif ":" in self.field_uri:
|
|
376
|
+
self.field_uri_postfix = self.field_uri.split(":")[1]
|
|
377
|
+
else:
|
|
378
|
+
self.field_uri_postfix = self.field_uri
|
|
379
|
+
|
|
380
|
+
def _get_val_from_elem(self, elem):
|
|
381
|
+
if self.is_attribute:
|
|
382
|
+
return elem.get(self.field_uri) or None
|
|
383
|
+
return get_xml_attr(elem, self.response_tag())
|
|
384
|
+
|
|
385
|
+
def from_xml(self, elem, account):
|
|
386
|
+
val = self._get_val_from_elem(elem)
|
|
387
|
+
if val is not None:
|
|
388
|
+
try:
|
|
389
|
+
return xml_text_to_value(val, self.value_cls)
|
|
390
|
+
except (ValueError, InvalidOperation):
|
|
391
|
+
log.warning("Cannot convert value '%s' on field '%s' to type %s", val, self.name, self.value_cls)
|
|
392
|
+
return None
|
|
393
|
+
return self.default
|
|
394
|
+
|
|
395
|
+
def to_xml(self, value, version):
|
|
396
|
+
field_elem = create_element(self.request_tag())
|
|
397
|
+
return set_xml_value(field_elem, value, version=version)
|
|
398
|
+
|
|
399
|
+
def field_uri_xml(self):
|
|
400
|
+
from .properties import FieldURI
|
|
401
|
+
|
|
402
|
+
if not self.field_uri:
|
|
403
|
+
raise ValueError(f"'field_uri' value is missing on field '{self.name}'")
|
|
404
|
+
return FieldURI(field_uri=self.field_uri).to_xml(version=None)
|
|
405
|
+
|
|
406
|
+
def request_tag(self):
|
|
407
|
+
if not self.field_uri_postfix:
|
|
408
|
+
raise ValueError(f"'field_uri_postfix' value is missing on field '{self.name}'")
|
|
409
|
+
return f"t:{self.field_uri_postfix}"
|
|
410
|
+
|
|
411
|
+
def response_tag(self):
|
|
412
|
+
if not self.field_uri_postfix:
|
|
413
|
+
raise ValueError(f"'field_uri_postfix' value is missing on field '{self.name}'")
|
|
414
|
+
return f"{{{self.namespace}}}{self.field_uri_postfix}"
|
|
415
|
+
|
|
416
|
+
def __hash__(self):
|
|
417
|
+
return hash(self.field_uri)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class BooleanField(FieldURIField):
|
|
421
|
+
"""A field that handles boolean values."""
|
|
422
|
+
|
|
423
|
+
value_cls = bool
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class IntegerField(FieldURIField):
|
|
427
|
+
"""A field that handles integer values."""
|
|
428
|
+
|
|
429
|
+
value_cls = int
|
|
430
|
+
|
|
431
|
+
def __init__(self, *args, **kwargs):
|
|
432
|
+
self.min = kwargs.pop("min", None)
|
|
433
|
+
self.max = kwargs.pop("max", None)
|
|
434
|
+
super().__init__(*args, **kwargs)
|
|
435
|
+
|
|
436
|
+
def _clean_single_value(self, v):
|
|
437
|
+
if self.min is not None and v < self.min:
|
|
438
|
+
raise ValueError(f"Value {v!r} on field {self.name!r} must be greater than {self.min}")
|
|
439
|
+
if self.max is not None and v > self.max:
|
|
440
|
+
raise ValueError(f"Value {v!r} on field {self.name!r} must be less than {self.max}")
|
|
441
|
+
|
|
442
|
+
def clean(self, value, version=None):
|
|
443
|
+
value = super().clean(value, version=version)
|
|
444
|
+
if value is not None:
|
|
445
|
+
if self.is_list:
|
|
446
|
+
for v in value:
|
|
447
|
+
self._clean_single_value(v)
|
|
448
|
+
else:
|
|
449
|
+
self._clean_single_value(value)
|
|
450
|
+
return value
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class DecimalField(IntegerField):
|
|
454
|
+
"""A field that handles decimal values."""
|
|
455
|
+
|
|
456
|
+
value_cls = Decimal
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class EnumField(IntegerField):
|
|
460
|
+
"""A field type where you can enter either the 1-based index in an enum (tuple), or the enum value. Values will be
|
|
461
|
+
stored internally as integers but output in XML as strings.
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
def __init__(self, *args, **kwargs):
|
|
465
|
+
self.enum = kwargs.pop("enum")
|
|
466
|
+
# Set different min/max defaults than IntegerField
|
|
467
|
+
if "max" in kwargs:
|
|
468
|
+
raise AttributeError("EnumField does not support the 'max' attribute")
|
|
469
|
+
kwargs["min"] = kwargs.pop("min", 1)
|
|
470
|
+
kwargs["max"] = kwargs["min"] + len(self.enum) - 1
|
|
471
|
+
super().__init__(*args, **kwargs)
|
|
472
|
+
|
|
473
|
+
def clean(self, value, version=None):
|
|
474
|
+
if self.is_list:
|
|
475
|
+
value = list(value) # Convert to something we can index
|
|
476
|
+
for i, v in enumerate(value):
|
|
477
|
+
if isinstance(v, str):
|
|
478
|
+
if v not in self.enum:
|
|
479
|
+
raise ValueError(f"List value {v!r} on field {self.name!r} must be one of {sorted(self.enum)}")
|
|
480
|
+
value[i] = self.enum.index(v) + 1
|
|
481
|
+
if not value:
|
|
482
|
+
raise ValueError(f"Value {value!r} on field {self.name!r} must not be empty")
|
|
483
|
+
if len(value) > len(set(value)):
|
|
484
|
+
raise ValueError(f"List entries {value!r} on field {self.name!r} must be unique")
|
|
485
|
+
else:
|
|
486
|
+
if isinstance(value, str):
|
|
487
|
+
if value not in self.enum:
|
|
488
|
+
raise ValueError(f"Value {value!r} on field {self.name!r} must be one of {sorted(self.enum)}")
|
|
489
|
+
value = self.enum.index(value) + 1
|
|
490
|
+
return super().clean(value, version=version)
|
|
491
|
+
|
|
492
|
+
def as_string(self, value):
|
|
493
|
+
# Converts an integer in the enum to its equivalent string
|
|
494
|
+
if self.is_list:
|
|
495
|
+
return [self.enum[v - 1] for v in sorted(value)]
|
|
496
|
+
return self.enum[value - 1]
|
|
497
|
+
|
|
498
|
+
def from_xml(self, elem, account):
|
|
499
|
+
val = self._get_val_from_elem(elem)
|
|
500
|
+
if val is not None:
|
|
501
|
+
try:
|
|
502
|
+
if self.is_list:
|
|
503
|
+
return [self.enum.index(v) + 1 for v in val.split(" ")]
|
|
504
|
+
return self.enum.index(val) + 1
|
|
505
|
+
except ValueError:
|
|
506
|
+
log.warning("Cannot convert value '%s' on field '%s' to type %s", val, self.name, self.value_cls)
|
|
507
|
+
return None
|
|
508
|
+
return self.default
|
|
509
|
+
|
|
510
|
+
def to_xml(self, value, version):
|
|
511
|
+
field_elem = create_element(self.request_tag())
|
|
512
|
+
if self.is_list:
|
|
513
|
+
return set_xml_value(field_elem, " ".join(self.as_string(value)), version=version)
|
|
514
|
+
return set_xml_value(field_elem, self.as_string(value), version=version)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
class EnumListField(EnumField):
|
|
518
|
+
"""Like EnumField, but for lists of enum values."""
|
|
519
|
+
|
|
520
|
+
is_list = True
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
class WeekdaysField(EnumListField):
|
|
524
|
+
"""Like EnumListField, allow a single value instead of a 1-element list."""
|
|
525
|
+
|
|
526
|
+
def clean(self, value, version=None):
|
|
527
|
+
if isinstance(value, (int, str)):
|
|
528
|
+
value = [value]
|
|
529
|
+
return super().clean(value, version)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
class EnumAsIntField(EnumField):
|
|
533
|
+
"""Like EnumField, but communicates values with EWS in integers."""
|
|
534
|
+
|
|
535
|
+
def from_xml(self, elem, account):
|
|
536
|
+
return super(EnumField, self).from_xml(elem=elem, account=account)
|
|
537
|
+
|
|
538
|
+
def to_xml(self, value, version):
|
|
539
|
+
field_elem = create_element(self.request_tag())
|
|
540
|
+
return set_xml_value(field_elem, value, version=version)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
class AppointmentStateField(IntegerField):
|
|
544
|
+
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/appointmentstate"""
|
|
545
|
+
|
|
546
|
+
NONE = "None"
|
|
547
|
+
MEETING = "Meeting"
|
|
548
|
+
RECEIVED = "Received"
|
|
549
|
+
CANCELLED = "Canceled"
|
|
550
|
+
STATES = {
|
|
551
|
+
NONE: 0x0000,
|
|
552
|
+
MEETING: 0x0001,
|
|
553
|
+
RECEIVED: 0x0002,
|
|
554
|
+
CANCELLED: 0x0004,
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
def from_xml(self, elem, account):
|
|
558
|
+
val = super().from_xml(elem=elem, account=account)
|
|
559
|
+
if val is None:
|
|
560
|
+
return val
|
|
561
|
+
return tuple(name for name, mask in self.STATES.items() if bool(val & mask))
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
class Base64Field(FieldURIField):
|
|
565
|
+
"""A field that handles binary data and automatically Base64 encodes and decodes the data."""
|
|
566
|
+
|
|
567
|
+
value_cls = bytes
|
|
568
|
+
is_complex = True
|
|
569
|
+
|
|
570
|
+
def __init__(self, *args, **kwargs):
|
|
571
|
+
if "is_searchable" not in kwargs:
|
|
572
|
+
kwargs["is_searchable"] = False
|
|
573
|
+
super().__init__(*args, **kwargs)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
class MimeContentField(Base64Field):
|
|
577
|
+
"""Like Base64Field. This element has an optional 'CharacterSet' attribute, but it specifies the encoding of the
|
|
578
|
+
base64-encoded string (which doesn't make sense since base64-encoded strings are always ASCII). We ignore it here
|
|
579
|
+
because the decoded data could be in some other encoding, specified in the "Content-Type" HTTP header.
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class DateField(FieldURIField):
|
|
584
|
+
"""A field that handles date values."""
|
|
585
|
+
|
|
586
|
+
value_cls = EWSDate
|
|
587
|
+
|
|
588
|
+
def clean(self, value, version=None):
|
|
589
|
+
# Allow plain datetime.date values as input
|
|
590
|
+
if type(value) is datetime.date:
|
|
591
|
+
value = self.value_cls.from_date(value)
|
|
592
|
+
return super().clean(value=value, version=version)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
class DateTimeBackedDateField(DateField):
|
|
596
|
+
"""A field that acts like a date, but where values are sent to EWS as EWSDateTime."""
|
|
597
|
+
|
|
598
|
+
def __init__(self, *args, **kwargs):
|
|
599
|
+
# Not all fields assume a default time of 00:00, so make this configurable
|
|
600
|
+
self._default_time = kwargs.pop("default_time", datetime.time(0, 0))
|
|
601
|
+
# Create internal field to handle datetime-only logic
|
|
602
|
+
self._datetime_field = DateTimeField(*args, **kwargs)
|
|
603
|
+
kwargs.pop("allow_naive", None)
|
|
604
|
+
super().__init__(*args, **kwargs)
|
|
605
|
+
|
|
606
|
+
def date_to_datetime(self, value):
|
|
607
|
+
return self._datetime_field.value_cls.combine(value, self._default_time).replace(tzinfo=UTC)
|
|
608
|
+
|
|
609
|
+
def from_xml(self, elem, account):
|
|
610
|
+
val = self._get_val_from_elem(elem)
|
|
611
|
+
if val is not None and len(val) == 25:
|
|
612
|
+
# This is a datetime string with timezone info, e.g. '2021-03-01T21:55:54+00:00'. We don't want to have
|
|
613
|
+
# datetime values converted to UTC before converting to date. EWSDateTime.from_string() insists on
|
|
614
|
+
# converting to UTC, but we don't have an EWSTimeZone we can convert the timezone info to. Instead, parse
|
|
615
|
+
# the string with .fromisoformat().
|
|
616
|
+
return datetime.datetime.fromisoformat(val).date()
|
|
617
|
+
# Revert to default parsing of datetime strings
|
|
618
|
+
res = self._datetime_field.from_xml(elem=elem, account=account)
|
|
619
|
+
if res is None:
|
|
620
|
+
return res
|
|
621
|
+
return res.date()
|
|
622
|
+
|
|
623
|
+
def to_xml(self, value, version):
|
|
624
|
+
# Convert date to datetime
|
|
625
|
+
value = self.date_to_datetime(value)
|
|
626
|
+
return self._datetime_field.to_xml(value=value, version=version)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class TimeField(FieldURIField):
|
|
630
|
+
"""A field that handles time values."""
|
|
631
|
+
|
|
632
|
+
value_cls = datetime.time
|
|
633
|
+
|
|
634
|
+
def from_xml(self, elem, account):
|
|
635
|
+
val = self._get_val_from_elem(elem)
|
|
636
|
+
if val is not None:
|
|
637
|
+
with suppress(ValueError):
|
|
638
|
+
if ":" in val:
|
|
639
|
+
# Assume a string of the form HH:MM:SS
|
|
640
|
+
return datetime.datetime.strptime(val, "%H:%M:%S").time()
|
|
641
|
+
# Assume an integer in minutes since midnight
|
|
642
|
+
return (datetime.datetime(2000, 1, 1) + datetime.timedelta(minutes=int(val))).time()
|
|
643
|
+
return self.default
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class TimeDeltaField(FieldURIField):
|
|
647
|
+
"""A field that handles timedelta values."""
|
|
648
|
+
|
|
649
|
+
value_cls = datetime.timedelta
|
|
650
|
+
|
|
651
|
+
def __init__(self, *args, **kwargs):
|
|
652
|
+
self.min = kwargs.pop("min", datetime.timedelta(0))
|
|
653
|
+
self.max = kwargs.pop("max", datetime.timedelta(days=1))
|
|
654
|
+
super().__init__(*args, **kwargs)
|
|
655
|
+
|
|
656
|
+
def clean(self, value, version=None):
|
|
657
|
+
if self.min is not None and value < self.min:
|
|
658
|
+
raise ValueError(f"Value {value!r} on field {self.name!r} must be greater than {self.min}")
|
|
659
|
+
if self.max is not None and value > self.max:
|
|
660
|
+
raise ValueError(f"Value {value!r} on field {self.name!r} must be less than {self.max}")
|
|
661
|
+
return super().clean(value, version=version)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
class DateTimeField(FieldURIField):
|
|
665
|
+
"""A field that handles datetime values."""
|
|
666
|
+
|
|
667
|
+
value_cls = EWSDateTime
|
|
668
|
+
|
|
669
|
+
def __init__(self, *args, **kwargs):
|
|
670
|
+
self.allow_naive = kwargs.pop("allow_naive", False)
|
|
671
|
+
super().__init__(*args, **kwargs)
|
|
672
|
+
|
|
673
|
+
def clean(self, value, version=None):
|
|
674
|
+
if isinstance(value, datetime.datetime):
|
|
675
|
+
if not value.tzinfo:
|
|
676
|
+
raise ValueError(f"Value {value!r} on field {self.name!r} must be timezone aware")
|
|
677
|
+
if type(value) is datetime.datetime:
|
|
678
|
+
value = self.value_cls.from_datetime(value)
|
|
679
|
+
return super().clean(value, version=version)
|
|
680
|
+
|
|
681
|
+
def from_xml(self, elem, account):
|
|
682
|
+
val = self._get_val_from_elem(elem)
|
|
683
|
+
if val is not None:
|
|
684
|
+
try:
|
|
685
|
+
return self.value_cls.from_string(val)
|
|
686
|
+
except ValueError as e:
|
|
687
|
+
if isinstance(e, NaiveDateTimeNotAllowed):
|
|
688
|
+
# We encountered a naive datetime
|
|
689
|
+
if account:
|
|
690
|
+
# Convert to timezone-aware datetime using the default timezone of the account
|
|
691
|
+
tz = account.default_timezone
|
|
692
|
+
log.info("Found naive datetime %s on field %s. Assuming timezone %s", e.local_dt, self.name, tz)
|
|
693
|
+
return e.local_dt.replace(tzinfo=tz)
|
|
694
|
+
if not self.allow_naive:
|
|
695
|
+
# There's nothing we can do but return the naive date. It's better than assuming e.g. UTC.
|
|
696
|
+
# Making this a hard error is probably too risky. Warn instead.
|
|
697
|
+
log.warning("Returning naive datetime %s on field %s", e.local_dt, self.name)
|
|
698
|
+
return e.local_dt
|
|
699
|
+
log.info("Cannot convert value '%s' on field '%s' to type %s", val, self.name, self.value_cls)
|
|
700
|
+
return None
|
|
701
|
+
return self.default
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
class DateOrDateTimeField(DateTimeField):
|
|
705
|
+
"""This field can handle both EWSDate and EWSDateTime. Used for calendar items where 'start' and 'end'
|
|
706
|
+
values are conceptually dates when the calendar item is an all-day event, but datetimes in all other cases, and
|
|
707
|
+
for recurrences where the returned 'start' and 'end' values may be either dates or datetimes depending on whether
|
|
708
|
+
the recurring item is a task or a calendar item.
|
|
709
|
+
|
|
710
|
+
For all-day calendar items, we assume both start and end dates are inclusive.
|
|
711
|
+
|
|
712
|
+
For filtering kwarg validation and other places where we must decide on a specific class, we settle on datetime.
|
|
713
|
+
"""
|
|
714
|
+
|
|
715
|
+
def __init__(self, *args, **kwargs):
|
|
716
|
+
super().__init__(*args, **kwargs)
|
|
717
|
+
# Create internal field to handle date-only logic
|
|
718
|
+
self._date_field = DateField(*args, **kwargs)
|
|
719
|
+
|
|
720
|
+
def clean(self, value, version=None):
|
|
721
|
+
# Most calendar items will contain datetime values. We can't access the is_all_day value here, so CalendarItem
|
|
722
|
+
# must handle that sanity check.
|
|
723
|
+
if type(value) in (EWSDate, datetime.date):
|
|
724
|
+
return self._date_field.clean(value=value, version=version)
|
|
725
|
+
return super().clean(value=value, version=version)
|
|
726
|
+
|
|
727
|
+
def from_xml(self, elem, account):
|
|
728
|
+
val = self._get_val_from_elem(elem)
|
|
729
|
+
if val is not None and len(val) in (11, 16):
|
|
730
|
+
# This is a date with timezone info, as sent by task recurrences. Eg: '2006-01-09+01:00' or '2006-01-09Z'
|
|
731
|
+
return self._date_field.from_xml(elem=elem, account=account)
|
|
732
|
+
return super().from_xml(elem=elem, account=account)
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
class TimeZoneField(FieldURIField):
|
|
736
|
+
"""A field that handles timezone values."""
|
|
737
|
+
|
|
738
|
+
value_cls = EWSTimeZone
|
|
739
|
+
|
|
740
|
+
def clean(self, value, version=None):
|
|
741
|
+
# Allow other timezone implementations as input
|
|
742
|
+
if value is not None:
|
|
743
|
+
value = self.value_cls.from_timezone(value)
|
|
744
|
+
return super().clean(value=value, version=version)
|
|
745
|
+
|
|
746
|
+
def from_xml(self, elem, account):
|
|
747
|
+
field_elem = elem.find(self.response_tag())
|
|
748
|
+
if field_elem is not None:
|
|
749
|
+
tz_id = field_elem.get("Id") or field_elem.get("Name")
|
|
750
|
+
try:
|
|
751
|
+
return self.value_cls.from_ms_id(tz_id)
|
|
752
|
+
except UnknownTimeZone:
|
|
753
|
+
warnings.warn(
|
|
754
|
+
f"""\
|
|
755
|
+
Cannot convert value {tz_id!r} on field {self.name!r} to type {self.value_cls.__name__!r} (unknown timezone ID).
|
|
756
|
+
You can fix this by adding a custom entry into the timezone translation map:
|
|
757
|
+
|
|
758
|
+
from exchangelib.winzone import MS_TIMEZONE_TO_IANA_MAP, CLDR_TO_MS_TIMEZONE_MAP
|
|
759
|
+
|
|
760
|
+
# Replace "Some_Region/Some_Location" with a reasonable value from CLDR_TO_MS_TIMEZONE_MAP.keys()
|
|
761
|
+
MS_TIMEZONE_TO_IANA_MAP[{tz_id!r}] = "Some_Region/Some_Location"
|
|
762
|
+
|
|
763
|
+
# Your code here"""
|
|
764
|
+
)
|
|
765
|
+
return None
|
|
766
|
+
return self.default
|
|
767
|
+
|
|
768
|
+
def to_xml(self, value, version):
|
|
769
|
+
attrs = dict(Id=value.ms_id)
|
|
770
|
+
if value.ms_name:
|
|
771
|
+
attrs["Name"] = value.ms_name
|
|
772
|
+
return create_element(self.request_tag(), attrs=attrs)
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
class TextField(FieldURIField):
|
|
776
|
+
"""A field that stores a string value with no length limit."""
|
|
777
|
+
|
|
778
|
+
value_cls = str
|
|
779
|
+
is_complex = True
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
class TextListField(TextField):
|
|
783
|
+
"""Like TextField, but for lists of text."""
|
|
784
|
+
|
|
785
|
+
is_list = True
|
|
786
|
+
|
|
787
|
+
def __init__(self, *args, **kwargs):
|
|
788
|
+
self.list_elem_name = kwargs.pop("list_elem_name", "String")
|
|
789
|
+
super().__init__(*args, **kwargs)
|
|
790
|
+
|
|
791
|
+
def list_elem_request_tag(self):
|
|
792
|
+
return f"t:{self.list_elem_name}"
|
|
793
|
+
|
|
794
|
+
def list_elem_response_tag(self):
|
|
795
|
+
return f"{{{self.namespace}}}{self.list_elem_name}"
|
|
796
|
+
|
|
797
|
+
def from_xml(self, elem, account):
|
|
798
|
+
iter_elem = elem.find(self.response_tag())
|
|
799
|
+
if iter_elem is not None:
|
|
800
|
+
return get_xml_attrs(iter_elem, self.list_elem_response_tag())
|
|
801
|
+
return self.default
|
|
802
|
+
|
|
803
|
+
def to_xml(self, value, version):
|
|
804
|
+
field_elem = create_element(self.request_tag())
|
|
805
|
+
for v in value:
|
|
806
|
+
field_elem.append(set_xml_value(create_element(self.list_elem_request_tag()), v, version=version))
|
|
807
|
+
return field_elem
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
class MessageField(TextField):
|
|
811
|
+
"""A field that handles the Message element."""
|
|
812
|
+
|
|
813
|
+
INNER_ELEMENT_NAME = "Message"
|
|
814
|
+
|
|
815
|
+
def from_xml(self, elem, account):
|
|
816
|
+
reply = elem.find(self.response_tag())
|
|
817
|
+
if reply is None:
|
|
818
|
+
return None
|
|
819
|
+
message = reply.find(f"{{{TNS}}}{self.INNER_ELEMENT_NAME}")
|
|
820
|
+
if message is None:
|
|
821
|
+
return None
|
|
822
|
+
return message.text
|
|
823
|
+
|
|
824
|
+
def to_xml(self, value, version):
|
|
825
|
+
field_elem = create_element(self.request_tag())
|
|
826
|
+
message = create_element(f"t:{self.INNER_ELEMENT_NAME}")
|
|
827
|
+
message.text = value
|
|
828
|
+
return set_xml_value(field_elem, message, version=version)
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
class CharField(TextField):
|
|
832
|
+
"""A field that stores a string value with a limited length."""
|
|
833
|
+
|
|
834
|
+
is_complex = False
|
|
835
|
+
|
|
836
|
+
def __init__(self, *args, **kwargs):
|
|
837
|
+
self.max_length = kwargs.pop("max_length", 255)
|
|
838
|
+
if not 1 <= self.max_length <= 255:
|
|
839
|
+
# A field supporting messages longer than 255 chars should be TextField
|
|
840
|
+
raise ValueError("'max_length' must be in the range 1-255")
|
|
841
|
+
super().__init__(*args, **kwargs)
|
|
842
|
+
|
|
843
|
+
def clean(self, value, version=None):
|
|
844
|
+
value = super().clean(value, version=version)
|
|
845
|
+
if value is not None and len(value) > self.max_length:
|
|
846
|
+
raise ValueError(f"{self.name!r} value {value!r} exceeds length {self.max_length}")
|
|
847
|
+
return value
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
class IdField(CharField):
|
|
851
|
+
"""A field to hold the 'Id' and 'Changekey' attributes on 'ItemId' type items. There is no guaranteed max length,
|
|
852
|
+
but we can assume 512 bytes in practice. See
|
|
853
|
+
https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/ews-identifiers-in-exchange
|
|
854
|
+
"""
|
|
855
|
+
|
|
856
|
+
def __init__(self, *args, **kwargs):
|
|
857
|
+
super().__init__(*args, **kwargs)
|
|
858
|
+
self.max_length = 512 # This is above the normal 255 limit, but this is actually an attribute, not a field
|
|
859
|
+
self.is_searchable = False
|
|
860
|
+
self.is_attribute = True
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
class CharListField(TextListField):
|
|
864
|
+
"""Like TextListField, but for string values with a limited length."""
|
|
865
|
+
|
|
866
|
+
def __init__(self, *args, **kwargs):
|
|
867
|
+
self.max_length = kwargs.pop("max_length", 255)
|
|
868
|
+
if not 1 <= self.max_length <= 255:
|
|
869
|
+
# A field supporting messages longer than 255 chars should be TextField
|
|
870
|
+
raise ValueError("'max_length' must be in the range 1-255")
|
|
871
|
+
super().__init__(*args, **kwargs)
|
|
872
|
+
|
|
873
|
+
def clean(self, value, version=None):
|
|
874
|
+
value = super().clean(value, version=version)
|
|
875
|
+
if value is not None:
|
|
876
|
+
for v in value:
|
|
877
|
+
if len(v) > self.max_length:
|
|
878
|
+
raise ValueError(f"{self.name!r} value {v!r} exceeds length {self.max_length}")
|
|
879
|
+
return value
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
class URIField(TextField):
|
|
883
|
+
"""Helper to mark strings that must conform to xsd:anyURI.
|
|
884
|
+
If we want a URI validator, see https://stackoverflow.com/questions/14466585/is-this-regex-correct-for-xsdanyuri
|
|
885
|
+
"""
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
class EmailAddressField(CharField):
|
|
889
|
+
"""A helper class used for email address string that we can use for email validation."""
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
class CultureField(CharField):
|
|
893
|
+
"""Helper to mark strings that are # RFC 1766 culture values."""
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
class Choice(SupportedVersionInstanceMixIn):
|
|
897
|
+
"""Implement versioned choices for the ChoiceField field."""
|
|
898
|
+
|
|
899
|
+
def __init__(self, value, *args, **kwargs):
|
|
900
|
+
self.value = value
|
|
901
|
+
super().__init__(*args, **kwargs)
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
class ChoiceField(CharField):
|
|
905
|
+
"""Like CharField, but restricts the value to a limited set of strings."""
|
|
906
|
+
|
|
907
|
+
def __init__(self, *args, **kwargs):
|
|
908
|
+
self.choices = kwargs.pop("choices")
|
|
909
|
+
super().__init__(*args, **kwargs)
|
|
910
|
+
|
|
911
|
+
def clean(self, value, version=None):
|
|
912
|
+
value = super().clean(value, version=version)
|
|
913
|
+
if value is None:
|
|
914
|
+
return None
|
|
915
|
+
valid_choices = [c.value for c in self.choices]
|
|
916
|
+
if version:
|
|
917
|
+
valid_choices_for_version = self.supported_choices(version=version)
|
|
918
|
+
if value in valid_choices_for_version:
|
|
919
|
+
return value
|
|
920
|
+
if value in valid_choices:
|
|
921
|
+
raise InvalidChoiceForVersion(
|
|
922
|
+
f"Choice {self.name!r} only supports server versions from {self.supported_from or '*'} to "
|
|
923
|
+
f"{self.deprecated_from or '*'} (server has {version})"
|
|
924
|
+
)
|
|
925
|
+
else:
|
|
926
|
+
if value in valid_choices:
|
|
927
|
+
return value
|
|
928
|
+
raise ValueError(f"Invalid choice {value!r} for field {self.name!r}. Valid choices are {sorted(valid_choices)}")
|
|
929
|
+
|
|
930
|
+
def supported_choices(self, version):
|
|
931
|
+
return tuple(c.value for c in self.choices if c.supports_version(version))
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
FREE_BUSY_CHOICES = [
|
|
935
|
+
Choice("Free"),
|
|
936
|
+
Choice("Tentative"),
|
|
937
|
+
Choice("Busy"),
|
|
938
|
+
Choice("OOF"),
|
|
939
|
+
Choice("NoData"),
|
|
940
|
+
Choice("WorkingElsewhere", supported_from=EXCHANGE_2013),
|
|
941
|
+
]
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
class FreeBusyStatusField(ChoiceField):
|
|
945
|
+
"""Like ChoiceField, but specifically for Free/Busy values."""
|
|
946
|
+
|
|
947
|
+
def __init__(self, *args, **kwargs):
|
|
948
|
+
kwargs["choices"] = set(FREE_BUSY_CHOICES)
|
|
949
|
+
super().__init__(*args, **kwargs)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
class BodyField(TextField):
|
|
953
|
+
"""A TextField with specific requirements for the Item body."""
|
|
954
|
+
|
|
955
|
+
def __init__(self, *args, **kwargs):
|
|
956
|
+
from .properties import Body
|
|
957
|
+
|
|
958
|
+
self.value_cls = Body
|
|
959
|
+
super().__init__(*args, **kwargs)
|
|
960
|
+
|
|
961
|
+
def clean(self, value, version=None):
|
|
962
|
+
if value is not None and not isinstance(value, self.value_cls):
|
|
963
|
+
value = self.value_cls(value)
|
|
964
|
+
return super().clean(value, version=version)
|
|
965
|
+
|
|
966
|
+
def from_xml(self, elem, account):
|
|
967
|
+
from .properties import Body, HTMLBody
|
|
968
|
+
|
|
969
|
+
field_elem = elem.find(self.response_tag())
|
|
970
|
+
val = None if field_elem is None else field_elem.text or None
|
|
971
|
+
if val is not None:
|
|
972
|
+
body_type = field_elem.get("BodyType")
|
|
973
|
+
return {Body.body_type: Body, HTMLBody.body_type: HTMLBody}[body_type](val)
|
|
974
|
+
return self.default
|
|
975
|
+
|
|
976
|
+
def to_xml(self, value, version):
|
|
977
|
+
from .properties import Body, HTMLBody
|
|
978
|
+
|
|
979
|
+
body_type = {
|
|
980
|
+
Body: Body.body_type,
|
|
981
|
+
HTMLBody: HTMLBody.body_type,
|
|
982
|
+
}[type(value)]
|
|
983
|
+
field_elem = create_element(self.request_tag(), attrs=dict(BodyType=body_type))
|
|
984
|
+
return set_xml_value(field_elem, value, version=version)
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
class EWSElementField(FieldURIField):
|
|
988
|
+
"""A generic field for any EWSElement object."""
|
|
989
|
+
|
|
990
|
+
def __init__(self, *args, **kwargs):
|
|
991
|
+
self._value_cls = kwargs.pop("value_cls")
|
|
992
|
+
if "namespace" not in kwargs:
|
|
993
|
+
kwargs["namespace"] = self.value_cls.NAMESPACE
|
|
994
|
+
super().__init__(*args, **kwargs)
|
|
995
|
+
|
|
996
|
+
@property
|
|
997
|
+
def value_cls(self):
|
|
998
|
+
if isinstance(self._value_cls, str):
|
|
999
|
+
# Support 'value_cls' as string to allow self-referencing classes. The class must be importable from the
|
|
1000
|
+
# top-level module.
|
|
1001
|
+
self._value_cls = getattr(import_module(self.__module__.split(".")[0]), self._value_cls)
|
|
1002
|
+
return self._value_cls
|
|
1003
|
+
|
|
1004
|
+
def from_xml(self, elem, account):
|
|
1005
|
+
if self.is_list:
|
|
1006
|
+
iter_elem = elem.find(self.response_tag())
|
|
1007
|
+
if iter_elem is not None:
|
|
1008
|
+
return [
|
|
1009
|
+
self.value_cls.from_xml(elem=e, account=account)
|
|
1010
|
+
for e in iter_elem.findall(self.value_cls.response_tag())
|
|
1011
|
+
]
|
|
1012
|
+
else:
|
|
1013
|
+
if self.field_uri is None:
|
|
1014
|
+
sub_elem = elem.find(self.value_cls.response_tag())
|
|
1015
|
+
else:
|
|
1016
|
+
sub_elem = elem.find(self.response_tag())
|
|
1017
|
+
if sub_elem is not None:
|
|
1018
|
+
return self.value_cls.from_xml(elem=sub_elem, account=account)
|
|
1019
|
+
return self.default
|
|
1020
|
+
|
|
1021
|
+
def to_xml(self, value, version):
|
|
1022
|
+
if self.field_uri is None:
|
|
1023
|
+
return value.to_xml(version=version)
|
|
1024
|
+
field_elem = create_element(self.request_tag())
|
|
1025
|
+
return set_xml_value(field_elem, value, version=version)
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
class EWSElementListField(EWSElementField):
|
|
1029
|
+
"""Like EWSElementField, but for lists of EWSElement objects."""
|
|
1030
|
+
|
|
1031
|
+
is_list = True
|
|
1032
|
+
is_complex = True
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
class TransitionListField(EWSElementListField):
|
|
1036
|
+
def __init__(self, *args, **kwargs):
|
|
1037
|
+
from .properties import BaseTransition
|
|
1038
|
+
|
|
1039
|
+
kwargs["value_cls"] = BaseTransition
|
|
1040
|
+
super().__init__(*args, **kwargs)
|
|
1041
|
+
|
|
1042
|
+
def from_xml(self, elem, account):
|
|
1043
|
+
iter_elem = elem.find(self.response_tag()) if self.field_uri else elem
|
|
1044
|
+
if iter_elem is not None:
|
|
1045
|
+
return [
|
|
1046
|
+
self.value_cls.transition_model_from_tag(e.tag).from_xml(elem=e, account=account) for e in iter_elem
|
|
1047
|
+
]
|
|
1048
|
+
return self.default
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
class AssociatedCalendarItemIdField(EWSElementField):
|
|
1052
|
+
is_complex = True
|
|
1053
|
+
|
|
1054
|
+
def __init__(self, *args, **kwargs):
|
|
1055
|
+
from .properties import AssociatedCalendarItemId
|
|
1056
|
+
|
|
1057
|
+
kwargs["value_cls"] = AssociatedCalendarItemId
|
|
1058
|
+
super().__init__(*args, **kwargs)
|
|
1059
|
+
|
|
1060
|
+
def to_xml(self, value, version):
|
|
1061
|
+
return value.to_xml(version=version)
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
class RecurrenceField(EWSElementField):
|
|
1065
|
+
is_complex = True
|
|
1066
|
+
|
|
1067
|
+
def __init__(self, *args, **kwargs):
|
|
1068
|
+
from .recurrence import Recurrence
|
|
1069
|
+
|
|
1070
|
+
kwargs["value_cls"] = Recurrence
|
|
1071
|
+
super().__init__(*args, **kwargs)
|
|
1072
|
+
|
|
1073
|
+
def to_xml(self, value, version):
|
|
1074
|
+
return value.to_xml(version=version)
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
class TaskRecurrenceField(EWSElementField):
|
|
1078
|
+
is_complex = True
|
|
1079
|
+
|
|
1080
|
+
def __init__(self, *args, **kwargs):
|
|
1081
|
+
from .recurrence import TaskRecurrence
|
|
1082
|
+
|
|
1083
|
+
kwargs["value_cls"] = TaskRecurrence
|
|
1084
|
+
super().__init__(*args, **kwargs)
|
|
1085
|
+
|
|
1086
|
+
def to_xml(self, value, version):
|
|
1087
|
+
return value.to_xml(version=version)
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
class ReferenceItemIdField(EWSElementField):
|
|
1091
|
+
is_complex = True
|
|
1092
|
+
|
|
1093
|
+
def __init__(self, *args, **kwargs):
|
|
1094
|
+
from .properties import ReferenceItemId
|
|
1095
|
+
|
|
1096
|
+
kwargs["value_cls"] = ReferenceItemId
|
|
1097
|
+
super().__init__(*args, **kwargs)
|
|
1098
|
+
|
|
1099
|
+
def to_xml(self, value, version):
|
|
1100
|
+
return value.to_xml(version=version)
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
class OccurrenceField(EWSElementField):
|
|
1104
|
+
is_complex = True
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
class OccurrenceListField(OccurrenceField):
|
|
1108
|
+
is_list = True
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
class MessageHeaderField(EWSElementListField):
|
|
1112
|
+
def __init__(self, *args, **kwargs):
|
|
1113
|
+
from .properties import MessageHeader
|
|
1114
|
+
|
|
1115
|
+
kwargs["value_cls"] = MessageHeader
|
|
1116
|
+
super().__init__(*args, **kwargs)
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
class BaseEmailField(EWSElementField, metaclass=abc.ABCMeta):
|
|
1120
|
+
"""Base class for EWSElement classes that have an 'email_address' field that we want to provide helpers for."""
|
|
1121
|
+
|
|
1122
|
+
is_complex = True # FindItem only returns the name, not the email address
|
|
1123
|
+
|
|
1124
|
+
def clean(self, value, version=None):
|
|
1125
|
+
if isinstance(value, str):
|
|
1126
|
+
value = self.value_cls(email_address=value)
|
|
1127
|
+
return super().clean(value, version=version)
|
|
1128
|
+
|
|
1129
|
+
def from_xml(self, elem, account):
|
|
1130
|
+
if self.field_uri is None:
|
|
1131
|
+
sub_elem = elem.find(self.value_cls.response_tag())
|
|
1132
|
+
else:
|
|
1133
|
+
sub_elem = elem.find(self.response_tag())
|
|
1134
|
+
if sub_elem is not None:
|
|
1135
|
+
if self.field_uri is not None:
|
|
1136
|
+
# We want the nested Mailbox, not the wrapper element
|
|
1137
|
+
nested_elem = sub_elem.find(self.value_cls.response_tag())
|
|
1138
|
+
if nested_elem is None:
|
|
1139
|
+
raise ValueError(
|
|
1140
|
+
f"Expected XML element {self.value_cls.response_tag()!r} missing on field {self.name!r}"
|
|
1141
|
+
)
|
|
1142
|
+
return self.value_cls.from_xml(elem=nested_elem, account=account)
|
|
1143
|
+
return self.value_cls.from_xml(elem=sub_elem, account=account)
|
|
1144
|
+
return self.default
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
class EmailField(BaseEmailField):
|
|
1148
|
+
def __init__(self, *args, **kwargs):
|
|
1149
|
+
from .properties import Email
|
|
1150
|
+
|
|
1151
|
+
kwargs["value_cls"] = Email
|
|
1152
|
+
super().__init__(*args, **kwargs)
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
class RecipientAddressField(BaseEmailField):
|
|
1156
|
+
def __init__(self, *args, **kwargs):
|
|
1157
|
+
from .properties import RecipientAddress
|
|
1158
|
+
|
|
1159
|
+
kwargs["value_cls"] = RecipientAddress
|
|
1160
|
+
super().__init__(*args, **kwargs)
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
class MailboxField(BaseEmailField):
|
|
1164
|
+
def __init__(self, *args, **kwargs):
|
|
1165
|
+
from .properties import Mailbox
|
|
1166
|
+
|
|
1167
|
+
kwargs["value_cls"] = Mailbox
|
|
1168
|
+
super().__init__(*args, **kwargs)
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
class MailboxListField(EWSElementListField):
|
|
1172
|
+
def __init__(self, *args, **kwargs):
|
|
1173
|
+
from .properties import Mailbox
|
|
1174
|
+
|
|
1175
|
+
kwargs["value_cls"] = Mailbox
|
|
1176
|
+
super().__init__(*args, **kwargs)
|
|
1177
|
+
|
|
1178
|
+
def clean(self, value, version=None):
|
|
1179
|
+
if value is not None:
|
|
1180
|
+
value = [self.value_cls(email_address=s) if isinstance(s, str) else s for s in value]
|
|
1181
|
+
return super().clean(value, version=version)
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
class AddressListField(EWSElementListField):
|
|
1185
|
+
def __init__(self, *args, **kwargs):
|
|
1186
|
+
from .properties import Address
|
|
1187
|
+
|
|
1188
|
+
kwargs["value_cls"] = Address
|
|
1189
|
+
super().__init__(*args, **kwargs)
|
|
1190
|
+
|
|
1191
|
+
def clean(self, value, version=None):
|
|
1192
|
+
if value is not None:
|
|
1193
|
+
value = [self.value_cls(email_address=s) if isinstance(s, str) else s for s in value]
|
|
1194
|
+
return super().clean(value, version=version)
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
class MemberListField(EWSElementListField):
|
|
1198
|
+
def __init__(self, *args, **kwargs):
|
|
1199
|
+
from .properties import Member
|
|
1200
|
+
|
|
1201
|
+
kwargs["value_cls"] = Member
|
|
1202
|
+
super().__init__(*args, **kwargs)
|
|
1203
|
+
|
|
1204
|
+
def clean(self, value, version=None):
|
|
1205
|
+
from .properties import Mailbox
|
|
1206
|
+
|
|
1207
|
+
if value is not None:
|
|
1208
|
+
value = [self.value_cls(mailbox=Mailbox(email_address=s)) if isinstance(s, str) else s for s in value]
|
|
1209
|
+
return super().clean(value, version=version)
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
class AttendeesField(EWSElementListField):
|
|
1213
|
+
def __init__(self, *args, **kwargs):
|
|
1214
|
+
from .properties import Attendee
|
|
1215
|
+
|
|
1216
|
+
kwargs["value_cls"] = Attendee
|
|
1217
|
+
super().__init__(*args, **kwargs)
|
|
1218
|
+
|
|
1219
|
+
def clean(self, value, version=None):
|
|
1220
|
+
from .properties import Mailbox
|
|
1221
|
+
|
|
1222
|
+
if value is not None:
|
|
1223
|
+
value = [
|
|
1224
|
+
self.value_cls(mailbox=Mailbox(email_address=s), response_type="Accept") if isinstance(s, str) else s
|
|
1225
|
+
for s in value
|
|
1226
|
+
]
|
|
1227
|
+
return super().clean(value, version=version)
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
class AttachmentField(EWSElementListField):
|
|
1231
|
+
"""A field for item attachments."""
|
|
1232
|
+
|
|
1233
|
+
def __init__(self, *args, **kwargs):
|
|
1234
|
+
from .attachments import Attachment
|
|
1235
|
+
|
|
1236
|
+
kwargs["value_cls"] = Attachment
|
|
1237
|
+
super().__init__(*args, **kwargs)
|
|
1238
|
+
|
|
1239
|
+
def from_xml(self, elem, account):
|
|
1240
|
+
from .attachments import FileAttachment, ItemAttachment
|
|
1241
|
+
|
|
1242
|
+
iter_elem = elem.find(self.response_tag())
|
|
1243
|
+
# Look for both FileAttachment and ItemAttachment
|
|
1244
|
+
if iter_elem is not None:
|
|
1245
|
+
attachments = []
|
|
1246
|
+
for att_type in (ItemAttachment, FileAttachment):
|
|
1247
|
+
attachments.extend(
|
|
1248
|
+
[att_type.from_xml(elem=e, account=account) for e in iter_elem.findall(att_type.response_tag())]
|
|
1249
|
+
)
|
|
1250
|
+
return attachments
|
|
1251
|
+
return self.default
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
class LabelField(ChoiceField):
|
|
1255
|
+
"""A field to hold the label on an IndexedElement."""
|
|
1256
|
+
|
|
1257
|
+
def __init__(self, *args, **kwargs):
|
|
1258
|
+
super().__init__(*args, **kwargs)
|
|
1259
|
+
self.is_attribute = True
|
|
1260
|
+
|
|
1261
|
+
def from_xml(self, elem, account):
|
|
1262
|
+
return elem.get(self.field_uri)
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
class SubField(Field):
|
|
1266
|
+
"""A field to hold the value on an IndexedElement."""
|
|
1267
|
+
|
|
1268
|
+
namespace = TNS
|
|
1269
|
+
value_cls = str
|
|
1270
|
+
|
|
1271
|
+
def from_xml(self, elem, account):
|
|
1272
|
+
return elem.text
|
|
1273
|
+
|
|
1274
|
+
def to_xml(self, value, version):
|
|
1275
|
+
return value
|
|
1276
|
+
|
|
1277
|
+
@staticmethod
|
|
1278
|
+
def field_uri_xml(field_uri, label):
|
|
1279
|
+
from .properties import IndexedFieldURI
|
|
1280
|
+
|
|
1281
|
+
return IndexedFieldURI(field_uri=field_uri, field_index=label).to_xml(version=None)
|
|
1282
|
+
|
|
1283
|
+
def clean(self, value, version=None):
|
|
1284
|
+
value = super().clean(value, version=version)
|
|
1285
|
+
if self.is_required and not value:
|
|
1286
|
+
raise ValueError(f"Value for subfield {self.name!r} must be non-empty")
|
|
1287
|
+
return value
|
|
1288
|
+
|
|
1289
|
+
def __hash__(self):
|
|
1290
|
+
return hash(self.name)
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
class EmailSubField(SubField):
|
|
1294
|
+
"""A field to hold the value on an SingleFieldIndexedElement."""
|
|
1295
|
+
|
|
1296
|
+
value_cls = str
|
|
1297
|
+
|
|
1298
|
+
def from_xml(self, elem, account):
|
|
1299
|
+
return elem.text or elem.get("Name") # Sometimes elem.text is empty. Exchange saves the same in 'Name' attr
|
|
1300
|
+
|
|
1301
|
+
|
|
1302
|
+
class NamedSubField(SubField):
|
|
1303
|
+
"""A field to hold the value on an MultiFieldIndexedElement."""
|
|
1304
|
+
|
|
1305
|
+
value_cls = str
|
|
1306
|
+
|
|
1307
|
+
def __init__(self, *args, **kwargs):
|
|
1308
|
+
self.field_uri = kwargs.pop("field_uri")
|
|
1309
|
+
if ":" in self.field_uri:
|
|
1310
|
+
raise ValueError("'field_uri' value must not contain a colon")
|
|
1311
|
+
super().__init__(*args, **kwargs)
|
|
1312
|
+
|
|
1313
|
+
def from_xml(self, elem, account):
|
|
1314
|
+
field_elem = elem.find(self.response_tag())
|
|
1315
|
+
val = None if field_elem is None else field_elem.text or None
|
|
1316
|
+
if val is not None:
|
|
1317
|
+
return val
|
|
1318
|
+
return self.default
|
|
1319
|
+
|
|
1320
|
+
def to_xml(self, value, version):
|
|
1321
|
+
field_elem = create_element(self.request_tag())
|
|
1322
|
+
return set_xml_value(field_elem, value, version=version)
|
|
1323
|
+
|
|
1324
|
+
def field_uri_xml(self, field_uri, label):
|
|
1325
|
+
from .properties import IndexedFieldURI
|
|
1326
|
+
|
|
1327
|
+
return IndexedFieldURI(field_uri=f"{field_uri}:{self.field_uri}", field_index=label).to_xml(version=None)
|
|
1328
|
+
|
|
1329
|
+
def request_tag(self):
|
|
1330
|
+
return f"t:{self.field_uri}"
|
|
1331
|
+
|
|
1332
|
+
def response_tag(self):
|
|
1333
|
+
return f"{{{self.namespace}}}{self.field_uri}"
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
class IndexedField(EWSElementField, metaclass=abc.ABCMeta):
|
|
1337
|
+
"""A base class for all indexed fields."""
|
|
1338
|
+
|
|
1339
|
+
is_list = True
|
|
1340
|
+
is_complex = True
|
|
1341
|
+
|
|
1342
|
+
PARENT_ELEMENT_NAME = None
|
|
1343
|
+
|
|
1344
|
+
def __init__(self, *args, **kwargs):
|
|
1345
|
+
from .indexed_properties import IndexedElement
|
|
1346
|
+
|
|
1347
|
+
value_cls = kwargs["value_cls"]
|
|
1348
|
+
if not issubclass(value_cls, IndexedElement):
|
|
1349
|
+
raise TypeError(f"'value_cls' {value_cls!r} must be a subclass of type {IndexedElement}")
|
|
1350
|
+
super().__init__(*args, **kwargs)
|
|
1351
|
+
|
|
1352
|
+
def to_xml(self, value, version):
|
|
1353
|
+
return set_xml_value(create_element(f"t:{self.PARENT_ELEMENT_NAME}"), value, version=version)
|
|
1354
|
+
|
|
1355
|
+
def response_tag(self):
|
|
1356
|
+
return f"{{{self.namespace}}}{self.PARENT_ELEMENT_NAME}"
|
|
1357
|
+
|
|
1358
|
+
def __hash__(self):
|
|
1359
|
+
return hash(self.field_uri)
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
class SingleFieldIndexedField(IndexedField):
|
|
1363
|
+
"""A base class for all single-field indexed fields."""
|
|
1364
|
+
|
|
1365
|
+
def __init__(self, *args, **kwargs):
|
|
1366
|
+
from .indexed_properties import SingleFieldIndexedElement
|
|
1367
|
+
|
|
1368
|
+
value_cls = kwargs["value_cls"]
|
|
1369
|
+
if not issubclass(value_cls, SingleFieldIndexedElement):
|
|
1370
|
+
raise TypeError(f"'value_cls' {value_cls!r} must be a subclass of type {SingleFieldIndexedElement}")
|
|
1371
|
+
super().__init__(*args, **kwargs)
|
|
1372
|
+
|
|
1373
|
+
def clean(self, value, version=None):
|
|
1374
|
+
if value is not None:
|
|
1375
|
+
default_labels = self.value_cls.LABEL_CHOICES
|
|
1376
|
+
if len(value) > len(default_labels):
|
|
1377
|
+
raise ValueError(f"This field can handle at most {len(default_labels)} values (value: {value})")
|
|
1378
|
+
tmp = []
|
|
1379
|
+
value_field_name = self.value_cls.value_field(version=version).name
|
|
1380
|
+
for s, default_label in zip(value, default_labels):
|
|
1381
|
+
if not isinstance(s, str):
|
|
1382
|
+
tmp.append(s)
|
|
1383
|
+
continue
|
|
1384
|
+
tmp.append(self.value_cls(**{"label": default_label, value_field_name: s}))
|
|
1385
|
+
value = tmp
|
|
1386
|
+
return super().clean(value, version=version)
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
class EmailAddressesField(SingleFieldIndexedField):
|
|
1390
|
+
PARENT_ELEMENT_NAME = "EmailAddresses"
|
|
1391
|
+
|
|
1392
|
+
def __init__(self, *args, **kwargs):
|
|
1393
|
+
from .indexed_properties import EmailAddress
|
|
1394
|
+
|
|
1395
|
+
kwargs["value_cls"] = EmailAddress
|
|
1396
|
+
super().__init__(*args, **kwargs)
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
class ImAddressField(SingleFieldIndexedField):
|
|
1400
|
+
PARENT_ELEMENT_NAME = "ImAddresses"
|
|
1401
|
+
|
|
1402
|
+
def __init__(self, *args, **kwargs):
|
|
1403
|
+
from .indexed_properties import ImAddress
|
|
1404
|
+
|
|
1405
|
+
kwargs["value_cls"] = ImAddress
|
|
1406
|
+
super().__init__(*args, **kwargs)
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
class PhoneNumberField(SingleFieldIndexedField):
|
|
1410
|
+
PARENT_ELEMENT_NAME = "PhoneNumbers"
|
|
1411
|
+
|
|
1412
|
+
def __init__(self, *args, **kwargs):
|
|
1413
|
+
from .indexed_properties import PhoneNumber
|
|
1414
|
+
|
|
1415
|
+
kwargs["value_cls"] = PhoneNumber
|
|
1416
|
+
super().__init__(*args, **kwargs)
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
class PhysicalAddressField(IndexedField):
|
|
1420
|
+
is_list = True
|
|
1421
|
+
is_complex = True
|
|
1422
|
+
|
|
1423
|
+
PARENT_ELEMENT_NAME = "PhysicalAddresses"
|
|
1424
|
+
|
|
1425
|
+
def __init__(self, *args, **kwargs):
|
|
1426
|
+
from .indexed_properties import PhysicalAddress
|
|
1427
|
+
|
|
1428
|
+
kwargs["value_cls"] = PhysicalAddress
|
|
1429
|
+
super().__init__(*args, **kwargs)
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
class ExtendedPropertyField(Field):
|
|
1433
|
+
is_complex = True
|
|
1434
|
+
|
|
1435
|
+
def __init__(self, *args, **kwargs):
|
|
1436
|
+
self.value_cls = kwargs.pop("value_cls")
|
|
1437
|
+
super().__init__(*args, **kwargs)
|
|
1438
|
+
|
|
1439
|
+
def clean(self, value, version=None):
|
|
1440
|
+
if value is None:
|
|
1441
|
+
if self.is_required:
|
|
1442
|
+
raise ValueError(f"{self.name!r} is a required field")
|
|
1443
|
+
return self.default
|
|
1444
|
+
if not isinstance(value, self.value_cls):
|
|
1445
|
+
# Allow keeping ExtendedProperty field values as their simple Python type, but run clean() anyway
|
|
1446
|
+
tmp = self.value_cls(value)
|
|
1447
|
+
tmp.clean(version=version)
|
|
1448
|
+
return value
|
|
1449
|
+
value.clean(version=version)
|
|
1450
|
+
return value
|
|
1451
|
+
|
|
1452
|
+
def field_uri_xml(self):
|
|
1453
|
+
from .properties import ExtendedFieldURI
|
|
1454
|
+
|
|
1455
|
+
cls = self.value_cls
|
|
1456
|
+
return ExtendedFieldURI(
|
|
1457
|
+
distinguished_property_set_id=cls.distinguished_property_set_id,
|
|
1458
|
+
property_set_id=cls.property_set_id.lower() if cls.property_set_id else None,
|
|
1459
|
+
property_tag=cls.property_tag_as_hex(),
|
|
1460
|
+
property_name=cls.property_name,
|
|
1461
|
+
property_id=value_to_xml_text(cls.property_id) if cls.property_id else None,
|
|
1462
|
+
property_type=cls.property_type,
|
|
1463
|
+
).to_xml(version=None)
|
|
1464
|
+
|
|
1465
|
+
def from_xml(self, elem, account):
|
|
1466
|
+
extended_properties = elem.findall(self.value_cls.response_tag())
|
|
1467
|
+
for extended_property in extended_properties:
|
|
1468
|
+
if self.value_cls.is_property_instance(extended_property):
|
|
1469
|
+
return self.value_cls.from_xml(elem=extended_property, account=account)
|
|
1470
|
+
return self.default
|
|
1471
|
+
|
|
1472
|
+
def to_xml(self, value, version):
|
|
1473
|
+
extended_property = create_element(self.value_cls.request_tag())
|
|
1474
|
+
set_xml_value(extended_property, self.field_uri_xml(), version=version)
|
|
1475
|
+
if isinstance(value, self.value_cls):
|
|
1476
|
+
return set_xml_value(extended_property, value, version=version)
|
|
1477
|
+
# Allow keeping ExtendedProperty field values as their simple Python type
|
|
1478
|
+
return set_xml_value(extended_property, self.value_cls(value), version=version)
|
|
1479
|
+
|
|
1480
|
+
def __hash__(self):
|
|
1481
|
+
return hash(self.name)
|
|
1482
|
+
|
|
1483
|
+
|
|
1484
|
+
class ExtendedPropertyListField(ExtendedPropertyField):
|
|
1485
|
+
is_list = True
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
class ItemField(FieldURIField):
|
|
1489
|
+
@property
|
|
1490
|
+
def value_cls(self):
|
|
1491
|
+
from .items import Item
|
|
1492
|
+
|
|
1493
|
+
return Item
|
|
1494
|
+
|
|
1495
|
+
def from_xml(self, elem, account):
|
|
1496
|
+
from .items import ITEM_CLASSES
|
|
1497
|
+
|
|
1498
|
+
for item_cls in ITEM_CLASSES:
|
|
1499
|
+
item_elem = elem.find(item_cls.response_tag())
|
|
1500
|
+
if item_elem is not None:
|
|
1501
|
+
return item_cls.from_xml(elem=item_elem, account=account)
|
|
1502
|
+
return None
|
|
1503
|
+
|
|
1504
|
+
def to_xml(self, value, version):
|
|
1505
|
+
# We don't want to wrap in an Item element
|
|
1506
|
+
return value.to_xml(version=version)
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
class UnknownEntriesField(CharListField):
|
|
1510
|
+
def list_elem_tag(self):
|
|
1511
|
+
return f"{{{self.namespace}}}UnknownEntry"
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
class PermissionSetField(EWSElementField):
|
|
1515
|
+
is_complex = True
|
|
1516
|
+
|
|
1517
|
+
def __init__(self, *args, **kwargs):
|
|
1518
|
+
from .properties import PermissionSet
|
|
1519
|
+
|
|
1520
|
+
kwargs["value_cls"] = PermissionSet
|
|
1521
|
+
super().__init__(*args, **kwargs)
|
|
1522
|
+
|
|
1523
|
+
def to_xml(self, value, version):
|
|
1524
|
+
return value.to_xml(version=version)
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
class EffectiveRightsField(EWSElementField):
|
|
1528
|
+
def __init__(self, *args, **kwargs):
|
|
1529
|
+
from .properties import EffectiveRights
|
|
1530
|
+
|
|
1531
|
+
kwargs["value_cls"] = EffectiveRights
|
|
1532
|
+
super().__init__(*args, **kwargs)
|
|
1533
|
+
|
|
1534
|
+
|
|
1535
|
+
class RoutingTypeField(ChoiceField):
|
|
1536
|
+
def __init__(self, *args, **kwargs):
|
|
1537
|
+
kwargs["choices"] = {Choice("SMTP"), Choice("EX")}
|
|
1538
|
+
kwargs["default"] = "SMTP"
|
|
1539
|
+
super().__init__(*args, **kwargs)
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
class IdElementField(EWSElementField):
|
|
1543
|
+
def __init__(self, *args, **kwargs):
|
|
1544
|
+
kwargs["is_searchable"] = False
|
|
1545
|
+
kwargs["is_read_only"] = True
|
|
1546
|
+
super().__init__(*args, **kwargs)
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
class TypeValueField(FieldURIField):
|
|
1550
|
+
"""This field type has no value_cls because values may have many types."""
|
|
1551
|
+
|
|
1552
|
+
TYPES_MAP = {
|
|
1553
|
+
"Boolean": bool,
|
|
1554
|
+
"Integer32": int,
|
|
1555
|
+
"UnsignedInteger32": int,
|
|
1556
|
+
"Integer64": int,
|
|
1557
|
+
"UnsignedInteger64": int,
|
|
1558
|
+
# Python doesn't have a single-byte type to represent 'Byte'
|
|
1559
|
+
"ByteArray": bytes,
|
|
1560
|
+
"String": str,
|
|
1561
|
+
"StringArray": str, # A list of strings
|
|
1562
|
+
"DateTime": EWSDateTime,
|
|
1563
|
+
}
|
|
1564
|
+
TYPES_MAP_REVERSED = {
|
|
1565
|
+
bool: "Boolean",
|
|
1566
|
+
int: "Integer64",
|
|
1567
|
+
# Python doesn't have a single-byte type to represent 'Byte'
|
|
1568
|
+
bytes: "ByteArray",
|
|
1569
|
+
str: "String",
|
|
1570
|
+
datetime.datetime: "DateTime",
|
|
1571
|
+
EWSDateTime: "DateTime",
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
@classmethod
|
|
1575
|
+
def get_type(cls, value):
|
|
1576
|
+
if isinstance(value, bytes) and len(value) == 1:
|
|
1577
|
+
# This is a single byte. Translate it to the 'Byte' type
|
|
1578
|
+
return "Byte"
|
|
1579
|
+
if is_iterable(value):
|
|
1580
|
+
# We don't allow generators as values, so keep the logic simple
|
|
1581
|
+
try:
|
|
1582
|
+
first = next(iter(value))
|
|
1583
|
+
except StopIteration:
|
|
1584
|
+
first = None
|
|
1585
|
+
value_type = f"{cls.TYPES_MAP_REVERSED[type(first)]}Array"
|
|
1586
|
+
if value_type not in cls.TYPES_MAP:
|
|
1587
|
+
raise ValueError(f"{value!r} is not a supported type")
|
|
1588
|
+
return value_type
|
|
1589
|
+
return cls.TYPES_MAP_REVERSED[type(value)]
|
|
1590
|
+
|
|
1591
|
+
@classmethod
|
|
1592
|
+
def is_array_type(cls, value_type):
|
|
1593
|
+
return value_type == "StringArray"
|
|
1594
|
+
|
|
1595
|
+
def clean(self, value, version=None):
|
|
1596
|
+
if value is None:
|
|
1597
|
+
if self.is_required and self.default is None:
|
|
1598
|
+
raise ValueError(f"{self.name!r} is a required field with no default")
|
|
1599
|
+
return self.default
|
|
1600
|
+
return value
|
|
1601
|
+
|
|
1602
|
+
def from_xml(self, elem, account):
|
|
1603
|
+
field_elem = elem.find(self.response_tag())
|
|
1604
|
+
if field_elem is None:
|
|
1605
|
+
return self.default
|
|
1606
|
+
value_type_str = get_xml_attr(field_elem, f"{{{TNS}}}Type")
|
|
1607
|
+
value = get_xml_attr(field_elem, f"{{{TNS}}}Value")
|
|
1608
|
+
if value_type_str == "Byte":
|
|
1609
|
+
try:
|
|
1610
|
+
# The value is an unsigned integer in the range 0 -> 255. Convert it to a single byte
|
|
1611
|
+
return xml_text_to_value(value, int).to_bytes(1, "little", signed=False)
|
|
1612
|
+
except OverflowError as e:
|
|
1613
|
+
log.warning("Invalid byte value %r (%e)", value, e)
|
|
1614
|
+
return None
|
|
1615
|
+
value_type = self.TYPES_MAP[value_type_str]
|
|
1616
|
+
if self.is_array_type(value_type_str):
|
|
1617
|
+
return tuple(xml_text_to_value(value=v, value_type=value_type) for v in value.split(" "))
|
|
1618
|
+
return xml_text_to_value(value=value, value_type=value_type)
|
|
1619
|
+
|
|
1620
|
+
def to_xml(self, value, version):
|
|
1621
|
+
value_type_str = self.get_type(value)
|
|
1622
|
+
if value_type_str == "Byte":
|
|
1623
|
+
# A single byte is encoded to an unsigned integer in the range 0 -> 255
|
|
1624
|
+
value = int.from_bytes(value, byteorder="little", signed=False)
|
|
1625
|
+
elif is_iterable(value):
|
|
1626
|
+
value = " ".join(value_to_xml_text(v) for v in value)
|
|
1627
|
+
field_elem = create_element(self.request_tag())
|
|
1628
|
+
field_elem.append(set_xml_value(create_element("t:Type"), value_type_str, version=version))
|
|
1629
|
+
field_elem.append(set_xml_value(create_element("t:Value"), value, version=version))
|
|
1630
|
+
return field_elem
|
|
1631
|
+
|
|
1632
|
+
|
|
1633
|
+
class DictionaryField(FieldURIField):
|
|
1634
|
+
value_cls = dict
|
|
1635
|
+
|
|
1636
|
+
def from_xml(self, elem, account):
|
|
1637
|
+
from .properties import DictionaryEntry
|
|
1638
|
+
|
|
1639
|
+
iter_elem = elem.find(self.response_tag())
|
|
1640
|
+
if iter_elem is not None:
|
|
1641
|
+
entries = [
|
|
1642
|
+
DictionaryEntry.from_xml(elem=e, account=account)
|
|
1643
|
+
for e in iter_elem.findall(DictionaryEntry.response_tag())
|
|
1644
|
+
]
|
|
1645
|
+
return {e.key: e.value for e in entries}
|
|
1646
|
+
return self.default
|
|
1647
|
+
|
|
1648
|
+
def clean(self, value, version=None):
|
|
1649
|
+
if isinstance(value, dict):
|
|
1650
|
+
cleaned = {}
|
|
1651
|
+
for k, v in value.items():
|
|
1652
|
+
if type(k) is datetime.datetime:
|
|
1653
|
+
k = EWSDateTime.from_datetime(k)
|
|
1654
|
+
if type(v) is datetime.datetime:
|
|
1655
|
+
v = EWSDateTime.from_datetime(v)
|
|
1656
|
+
cleaned[k] = v
|
|
1657
|
+
value = cleaned
|
|
1658
|
+
return super().clean(value=value, version=version)
|
|
1659
|
+
|
|
1660
|
+
def to_xml(self, value, version):
|
|
1661
|
+
from .properties import DictionaryEntry
|
|
1662
|
+
|
|
1663
|
+
field_elem = create_element(self.request_tag())
|
|
1664
|
+
entries = [DictionaryEntry(key=k, value=v) for k, v in value.items()]
|
|
1665
|
+
return set_xml_value(field_elem, entries, version=version)
|
|
1666
|
+
|
|
1667
|
+
|
|
1668
|
+
class PersonaPhoneNumberField(EWSElementField):
|
|
1669
|
+
is_complex = True
|
|
1670
|
+
|
|
1671
|
+
def __init__(self, *args, **kwargs):
|
|
1672
|
+
from .properties import PhoneNumber
|
|
1673
|
+
|
|
1674
|
+
kwargs["value_cls"] = PhoneNumber
|
|
1675
|
+
super().__init__(*args, **kwargs)
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
class BodyContentAttributedValueField(EWSElementField):
|
|
1679
|
+
is_complex = True
|
|
1680
|
+
|
|
1681
|
+
def __init__(self, *args, **kwargs):
|
|
1682
|
+
from .properties import BodyContentAttributedValue
|
|
1683
|
+
|
|
1684
|
+
kwargs["value_cls"] = BodyContentAttributedValue
|
|
1685
|
+
super().__init__(*args, **kwargs)
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
class StringAttributedValueField(EWSElementListField):
|
|
1689
|
+
def __init__(self, *args, **kwargs):
|
|
1690
|
+
from .properties import StringAttributedValue
|
|
1691
|
+
|
|
1692
|
+
kwargs["value_cls"] = StringAttributedValue
|
|
1693
|
+
super().__init__(*args, **kwargs)
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
class PhoneNumberAttributedValueField(EWSElementListField):
|
|
1697
|
+
def __init__(self, *args, **kwargs):
|
|
1698
|
+
from .properties import PhoneNumberAttributedValue
|
|
1699
|
+
|
|
1700
|
+
kwargs["value_cls"] = PhoneNumberAttributedValue
|
|
1701
|
+
super().__init__(*args, **kwargs)
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
class EmailAddressAttributedValueField(EWSElementListField):
|
|
1705
|
+
def __init__(self, *args, **kwargs):
|
|
1706
|
+
from .properties import EmailAddressAttributedValue
|
|
1707
|
+
|
|
1708
|
+
kwargs["value_cls"] = EmailAddressAttributedValue
|
|
1709
|
+
super().__init__(*args, **kwargs)
|
|
1710
|
+
|
|
1711
|
+
|
|
1712
|
+
class PostalAddressAttributedValueField(EWSElementListField):
|
|
1713
|
+
def __init__(self, *args, **kwargs):
|
|
1714
|
+
from .properties import PostalAddressAttributedValue
|
|
1715
|
+
|
|
1716
|
+
kwargs["value_cls"] = PostalAddressAttributedValue
|
|
1717
|
+
super().__init__(*args, **kwargs)
|
|
1718
|
+
|
|
1719
|
+
|
|
1720
|
+
class GenericEventListField(EWSElementField):
|
|
1721
|
+
"""A list field that can contain all subclasses of Event."""
|
|
1722
|
+
|
|
1723
|
+
is_list = True
|
|
1724
|
+
|
|
1725
|
+
@property
|
|
1726
|
+
def _event_types_map(self):
|
|
1727
|
+
return {v.response_tag(): v for v in self.value_classes}
|
|
1728
|
+
|
|
1729
|
+
def __init__(self, *args, **kwargs):
|
|
1730
|
+
from .properties import (
|
|
1731
|
+
CopiedEvent,
|
|
1732
|
+
CreatedEvent,
|
|
1733
|
+
DeletedEvent,
|
|
1734
|
+
FreeBusyChangedEvent,
|
|
1735
|
+
ModifiedEvent,
|
|
1736
|
+
MovedEvent,
|
|
1737
|
+
NewMailEvent,
|
|
1738
|
+
StatusEvent,
|
|
1739
|
+
)
|
|
1740
|
+
|
|
1741
|
+
kwargs["value_cls"] = None # Parent class requires this kwarg
|
|
1742
|
+
kwargs["namespace"] = None # Parent class requires this kwarg
|
|
1743
|
+
super().__init__(*args, **kwargs)
|
|
1744
|
+
self.value_classes = (
|
|
1745
|
+
CopiedEvent,
|
|
1746
|
+
CreatedEvent,
|
|
1747
|
+
DeletedEvent,
|
|
1748
|
+
ModifiedEvent,
|
|
1749
|
+
MovedEvent,
|
|
1750
|
+
NewMailEvent,
|
|
1751
|
+
StatusEvent,
|
|
1752
|
+
FreeBusyChangedEvent,
|
|
1753
|
+
)
|
|
1754
|
+
|
|
1755
|
+
def from_xml(self, elem, account):
|
|
1756
|
+
events = []
|
|
1757
|
+
for event in elem:
|
|
1758
|
+
# This may or may not be an event element. Could also be other child elements of Notification
|
|
1759
|
+
try:
|
|
1760
|
+
value_cls = self._event_types_map[event.tag]
|
|
1761
|
+
except KeyError:
|
|
1762
|
+
continue
|
|
1763
|
+
events.append(value_cls.from_xml(elem=event, account=account))
|
|
1764
|
+
return events or self.default
|
|
1765
|
+
|
|
1766
|
+
|
|
1767
|
+
FLAG_ACTION_CHOICES = [
|
|
1768
|
+
Choice("Any"),
|
|
1769
|
+
Choice("Call"),
|
|
1770
|
+
Choice("DoNotForward"),
|
|
1771
|
+
Choice("FollowUp"),
|
|
1772
|
+
Choice("FYI"),
|
|
1773
|
+
Choice("Forward"),
|
|
1774
|
+
Choice("NoResponseNecessary"),
|
|
1775
|
+
Choice("Read"),
|
|
1776
|
+
Choice("Reply"),
|
|
1777
|
+
Choice("ReplyToAll"),
|
|
1778
|
+
Choice("Review"),
|
|
1779
|
+
]
|
|
1780
|
+
|
|
1781
|
+
|
|
1782
|
+
class FlaggedForActionField(ChoiceField):
|
|
1783
|
+
"""
|
|
1784
|
+
A field specifies the flag for action value that
|
|
1785
|
+
must appear on incoming messages in order for the condition
|
|
1786
|
+
or exception to apply.
|
|
1787
|
+
"""
|
|
1788
|
+
|
|
1789
|
+
def __init__(self, *args, **kwargs):
|
|
1790
|
+
kwargs["choices"] = FLAG_ACTION_CHOICES
|
|
1791
|
+
super().__init__(*args, **kwargs)
|
|
1792
|
+
|
|
1793
|
+
|
|
1794
|
+
IMPORTANCE_CHOICES = [
|
|
1795
|
+
Choice("Low"),
|
|
1796
|
+
Choice("Normal"),
|
|
1797
|
+
Choice("High"),
|
|
1798
|
+
]
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
class ImportanceField(ChoiceField):
|
|
1802
|
+
"""
|
|
1803
|
+
A field that describes the importance of an item or
|
|
1804
|
+
the aggregated importance of all items in a conversation
|
|
1805
|
+
in the current folder.
|
|
1806
|
+
"""
|
|
1807
|
+
|
|
1808
|
+
def __init__(self, *args, **kwargs):
|
|
1809
|
+
kwargs["choices"] = IMPORTANCE_CHOICES
|
|
1810
|
+
super().__init__(*args, **kwargs)
|
|
1811
|
+
|
|
1812
|
+
|
|
1813
|
+
SENSITIVITY_CHOICES = [
|
|
1814
|
+
Choice("Normal"),
|
|
1815
|
+
Choice("Personal"),
|
|
1816
|
+
Choice("Private"),
|
|
1817
|
+
Choice("Confidential"),
|
|
1818
|
+
]
|
|
1819
|
+
|
|
1820
|
+
|
|
1821
|
+
class SensitivityField(ChoiceField):
|
|
1822
|
+
"""A field that indicates the sensitivity level of an item."""
|
|
1823
|
+
|
|
1824
|
+
def __init__(self, *args, **kwargs):
|
|
1825
|
+
kwargs["choices"] = SENSITIVITY_CHOICES
|
|
1826
|
+
super().__init__(*args, **kwargs)
|
|
1827
|
+
|
|
1828
|
+
|
|
1829
|
+
class FolderActionField(EWSElementField):
|
|
1830
|
+
def __init__(self, *args, **kwargs):
|
|
1831
|
+
super().__init__(*args, **kwargs)
|
|
1832
|
+
|
|
1833
|
+
def clean(self, value, version=None):
|
|
1834
|
+
from .folders import DistinguishedFolderId, Folder
|
|
1835
|
+
|
|
1836
|
+
if isinstance(value, Folder):
|
|
1837
|
+
folder_id = value.to_id()
|
|
1838
|
+
if isinstance(folder_id, DistinguishedFolderId):
|
|
1839
|
+
value = self.value_cls(distinguished_folder_id=folder_id)
|
|
1840
|
+
else:
|
|
1841
|
+
value = self.value_cls(folder_id=folder_id)
|
|
1842
|
+
return super().clean(value, version=version)
|